diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# Ignore the following files
+*~
+*.[oa]
+*.diff
+*.kate-swp
+*.kdev4
+.kdev_include_paths
+*.kdevelop.pcs
+*.moc
+*.moc.cpp
+*.orig
+*.user
+.*.swp
+.swp.*
+Doxyfile
+Makefile
+avail
+random_seed
+/build*/
+CMakeLists.txt.user*
+*.unc-backup*
diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.0)
project(plasma-workspace)
-set(PROJECT_VERSION "5.16.80")
+set(PROJECT_VERSION "5.17.80")
set(PROJECT_VERSION_MAJOR 5)
set(QT_MIN_VERSION "5.12.0")
-set(KF5_MIN_VERSION "5.58.0")
+set(KF5_MIN_VERSION "5.62.0")
set(INSTALL_SDDM_THEME TRUE)
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Concurrent Test Network)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
@@ -21,12 +21,14 @@
include(FeatureSummary)
include(ECMOptionalAddSubdirectory)
include(ECMQtDeclareLoggingCategory)
+include(ECMQueryQmake)
include(KDEPackageAppTemplates)
+include(KDEClangFormat)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
- Plasma DocTools Runner JsEmbed NotifyConfig Su NewStuff Wallet
+ Plasma DocTools Runner NotifyConfig Su NewStuff Wallet
IdleTime Declarative I18n KCMUtils TextWidgets KDELibs4Support Crash GlobalAccel
- DBusAddons Wayland CoreAddons)
+ DBusAddons Wayland CoreAddons People ActivitiesStats)
find_package(KDED CONFIG REQUIRED)
find_package(KF5NetworkManagerQt ${KF5_MIN_VERSION})
@@ -41,7 +43,14 @@
TYPE RUNTIME
)
+find_package(KF5QuickCharts ${KF5_MIN_VERSION} CONFIG)
+set_package_properties(KF5QuickCharts PROPERTIES
+ DESCRIPTION "Used for rendering charts"
+ TYPE RUNTIME
+)
+
# WARNING PlasmaQuick provides unversioned CMake config
+find_package(KUserFeedback)
find_package(KF5 REQUIRED COMPONENTS PlasmaQuick)
find_package(KF5 REQUIRED COMPONENTS SysGuard)
find_package(KF5 REQUIRED COMPONENTS Package)
@@ -55,6 +64,7 @@
find_package(KF5TextEditor)
find_package(KWinDBusInterface CONFIG REQUIRED)
+find_package(KF5Screen CONFIG REQUIRED)
find_package(KScreenLocker 5.13.80 REQUIRED)
find_package(ScreenSaverDBusInterface CONFIG REQUIRED)
find_package(KF5Holidays)
@@ -75,13 +85,13 @@
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES DESCRIPTION "Support for gzip compressed files and data streams"
- URL "http://www.zlib.net"
+ URL "https://www.zlib.net"
TYPE REQUIRED
)
find_package(X11)
set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
- URL "http://www.x.org"
+ URL "https://www.x.org"
TYPE OPTIONAL
PURPOSE "Required for building the X11 based workspace")
@@ -105,14 +115,18 @@
TYPE OPTIONAL
)
+if(${AppStreamQt_FOUND})
+ set(HAVE_APPSTREAMQT true)
+endif()
include(ConfigureChecks.cmake)
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h)
configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h )
configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h)
+configure_file(config-appstream.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-appstream.h )
add_subdirectory(login-sessions)
plasma_install_package(lookandfeel org.kde.breeze.desktop look-and-feel lookandfeel)
@@ -132,6 +146,9 @@
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
+# locate qdbus in the Qt path because not every distro makes a symlink at /usr/bin/qdbus
+query_qmake(QtBinariesDir QT_INSTALL_BINS)
+
add_subdirectory(doc)
add_subdirectory(libkworkspace)
add_subdirectory(libdbusmenuqt)
@@ -187,9 +204,9 @@
add_subdirectory(templates)
-if (${ECM_VERSION} STRGREATER "5.58.0")
- install(FILES plasma-workspace.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
-else()
- install(FILES plasma-workspace.categories DESTINATION ${KDE_INSTALL_CONFDIR})
-endif()
+install(FILES plasma-workspace.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
+
+# add clang-format target for all our real source files
+file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h)
+kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
diff --git a/applets/CMakeLists.txt b/applets/CMakeLists.txt
--- a/applets/CMakeLists.txt
+++ b/applets/CMakeLists.txt
@@ -12,6 +12,8 @@
add_subdirectory(calendar)
add_subdirectory(devicenotifier)
add_subdirectory(digital-clock)
+add_subdirectory(kicker)
+
plasma_install_package(clipboard org.kde.plasma.clipboard)
if(NOT WIN32)
diff --git a/applets/activitybar/metadata.desktop b/applets/activitybar/metadata.desktop
--- a/applets/activitybar/metadata.desktop
+++ b/applets/activitybar/metadata.desktop
@@ -1,6 +1,7 @@
[Desktop Entry]
Name=Activity Bar
Name[ar]=شريط أنشطة
+Name[ast]=Barra d'actividaes
Name[be@latin]=Panel zaniatkaŭ
Name[bg]=Лента за активност
Name[bs]=Traka aktivnosti
@@ -62,7 +63,7 @@
Name[sv]=Aktivitetsrad
Name[ta]=Activity Bar
Name[te]=క్రియాశీలత పట్టీ
-Name[tg]=Панели фаъолиятӣ
+Name[tg]=Навори фаъолият
Name[th]=แถบกิจกรรม
Name[tr]=Etkinlik Çubuğu
Name[ug]=پائالىيەت بالدىقى
@@ -91,7 +92,7 @@
Comment[fr]=Barre d'onglets permettant de changer d'activité
Comment[fy]=Ljepper om tusken aktiviteiten te wikseljen
Comment[ga]=Barra na gcluaisíní chun an ghníomhaíocht a mhalartú
-Comment[gl]=Barra de lapelas para cambiar entre actividades
+Comment[gl]=Barra de separadores para cambiar entre actividades
Comment[gu]=ક્રિયાઓ બદલવા માટે ટેબ પટ્ટી
Comment[he]=סרגל לשוניות למעבר בין פעילויות
Comment[hi]=क्रिया स्विच करने के लिए टैबपट्टी
@@ -106,7 +107,7 @@
Comment[km]=របារផ្ទាំងដើម្បីប្ដូរសកម្មភាព
Comment[kn]=ಚಟುವಟಿಕೆಗಳನ್ನು ಬದಲಾಯಿಸುವ ಟ್ಯಾಬ್ ಪಟ್ಟಿ
Comment[ko]=활동 사이를 전환할 수 있는 탭 표시줄
-Comment[lt]=Kortelių juosta veiklų keitimui
+Comment[lt]=Kortelių juosta, skirta perjungti veiklas
Comment[lv]=Ciļnu josla aktivitāšu pārslēgšanai
Comment[mai]=क्रियाकलाप स्विच करबाक लेल टैबपट्टी
Comment[ml]=പ്രവര്ത്തനങ്ങള് മാറ്റാന് ടാബ്-ബാര്
@@ -129,7 +130,6 @@
Comment[sr@ijekavianlatin]=Traka jezičaka za prebacivanje aktivnosti
Comment[sr@latin]=Traka jezičaka za prebacivanje aktivnosti
Comment[sv]=Flikrad för att byta aktiviteter
-Comment[tg]=Переключиться в командную строку
Comment[th]=แถบแท็บเพื่อใช้สลับกิจกรรม
Comment[tr]=Eylemleri seçmek için sekme çubuğu
Comment[ug]=پائالىيەت ئالماشتۇرىدىغان بەتكۈچ بالداق
diff --git a/applets/analog-clock/metadata.desktop b/applets/analog-clock/metadata.desktop
--- a/applets/analog-clock/metadata.desktop
+++ b/applets/analog-clock/metadata.desktop
@@ -2,6 +2,7 @@
Name=Analog Clock
Name[af]=Analooghorlosie
Name[ar]=ساعة تناظرية
+Name[ast]=Reló analóxicu
Name[be]=Аналагавы гадзіннік
Name[be@latin]=Analahavy hadzińnik
Name[bg]=Аналогов часовник
@@ -85,6 +86,7 @@
Name[zh_TW]=類比時鐘
Comment=A clock with hands
Comment[ar]=ساعة بِعقارب
+Comment[ast]=Un reló con aguyes
Comment[bg]=Часовник със стрелки
Comment[bs]=Sat sa kazaljkama
Comment[ca]=Un rellotge amb agulles
@@ -97,7 +99,7 @@
Comment[eo]=Horloĝo kun manoj
Comment[es]=Un reloj con manecillas
Comment[et]=Seieritega kell
-Comment[eu]=Erloju orrazduna
+Comment[eu]=Erloju orraztuna
Comment[fi]=Viisarikello
Comment[fr]=Une horloge à aiguilles
Comment[fy]=In klok mei wizerplaat
@@ -138,7 +140,6 @@
Comment[sr@ijekavianlatin]=Sat sa kazaljkama
Comment[sr@latin]=Sat sa kazaljkama
Comment[sv]=En klocka med visare
-Comment[tg]=Соат бо ақрабакҳо
Comment[th]=นาฬิกาแบบเข็ม
Comment[tr]=İbreli bir saat
Comment[ug]=تىللىق سائەت
@@ -162,7 +163,7 @@
X-KDE-PluginInfo-Email=mart@kde.org
X-KDE-PluginInfo-Name=org.kde.plasma.analogclock
X-KDE-PluginInfo-Version=3.0
-X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/Clocks
+X-KDE-PluginInfo-Website=https://userbase.kde.org/Plasma/Clocks
X-KDE-PluginInfo-Category=Date and Time
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL-2.0+
diff --git a/applets/appmenu/lib/appmenuapplet.cpp b/applets/appmenu/lib/appmenuapplet.cpp
--- a/applets/appmenu/lib/appmenuapplet.cpp
+++ b/applets/appmenu/lib/appmenuapplet.cpp
@@ -55,7 +55,7 @@
another destroyedchanged and destroyed will be false.
When this happens, if we are the only appmenu applet existing, the dbus interface
will have to be registered again*/
- connect(this, &Applet::destroyedChanged, this, [this](bool destroyed) {
+ connect(this, &Applet::destroyedChanged, this, [](bool destroyed) {
if (destroyed) {
//if we were the last, unregister
if (--s_refs == 0) {
diff --git a/applets/appmenu/package/contents/ui/main.qml b/applets/appmenu/package/contents/ui/main.qml
--- a/applets/appmenu/package/contents/ui/main.qml
+++ b/applets/appmenu/package/contents/ui/main.qml
@@ -18,7 +18,7 @@
*/
import QtQuick 2.0
import QtQuick.Layouts 1.1
-import QtQuick.Controls 1.4
+import QtQuick.Controls 2.8
import org.kde.plasma.plasmoid 2.0
import org.kde.kquickcontrolsaddons 2.0
diff --git a/applets/appmenu/package/metadata.desktop b/applets/appmenu/package/metadata.desktop
--- a/applets/appmenu/package/metadata.desktop
+++ b/applets/appmenu/package/metadata.desktop
@@ -1,14 +1,16 @@
[Desktop Entry]
Name=Global Menu
Name[ar]=القائمة العموميّة
+Name[ast]=Menú global
Name[ca]=Menú global
Name[ca@valencia]=Menú global
Name[cs]=Globální nabídka
Name[da]=Global menu
Name[de]=Globales Menü
Name[el]=Καθολικό μενού
Name[en_GB]=Global Menu
Name[es]=Menú global
+Name[et]=Globaalne menüü
Name[eu]=Menu orokorra
Name[fi]=Yleisvalikko
Name[fr]=Menu global
@@ -19,49 +21,53 @@
Name[it]=Menu globale
Name[ko]=전역 메뉴
Name[lt]=Visuotinis meniu
+Name[lv]=Globālā izvēlne
Name[nl]=Globaal menu
Name[nn]=Global meny
Name[pa]=ਗਲੋਬਲ ਮੇਨੂ
Name[pl]=Menu globalne
Name[pt]=Menu Global
Name[pt_BR]=Menu global
Name[ru]=Меню приложения
-Name[sk]=Globálne menu
+Name[sk]=Globálna ponuka
Name[sl]=Splošni meni
Name[sr]=Глобални мени
Name[sr@ijekavian]=Глобални мени
Name[sr@ijekavianlatin]=Globalni meni
Name[sr@latin]=Globalni meni
Name[sv]=Global meny
+Name[tg]=Феҳристи умумӣ
Name[tr]=Genel Menü
Name[uk]=Загальне меню
Name[x-test]=xxGlobal Menuxx
Name[zh_CN]=全局菜单
Name[zh_TW]=全域選單
Comment=Global menubar on top of the screen
Comment[ar]=شريط القوائم العموميّ أعلى الشّاشة
Comment[ca]=Barra de menús global a la part superior de la pantalla
-Comment[ca@valencia]=Barra de menús global a la part superior de la pantalla
+Comment[ca@valencia]=Barra de menús global en la part superior de la pantalla
Comment[da]=Global menulinje øverst på skærmen
Comment[de]=Globale Menüleiste oben auf dem Bildschirm
Comment[el]=Καθολική μπάρα μενού στη κορυφή της οθόνης
Comment[en_GB]=Global menubar on top of the screen
Comment[es]=Barra de menú global en la parte superior de la pantalla
+Comment[et]=Globaalne menüü töölaua ülaservas
Comment[eu]=Menu-barra orokorra pantailaren gainaldean
Comment[fi]=Työpöydänlaajuinen valikkorivi ruudun ylälaidassa
Comment[fr]=Barre de menu globale en haut de l'écran
Comment[gl]=Barra de menú global na parte superior da pantalla.
Comment[hu]=Globális menüsáv a képernyő tetején
Comment[id]=Bilah menu global berada di atas layar
Comment[it]=Barra dei menu globale nella parte alta dello schermo
Comment[ko]=화면 위에 전역 메뉴 표시줄 보이기
+Comment[lt]=Visuotinė meniu juosta ekrano viršuje
Comment[nl]=Globale menubalk bovenaan het scherm
Comment[nn]=Global menylinje øvst på skjermen
Comment[pl]=Globalny pasek menu na górze ekranu
Comment[pt]=Menu global no topo do ecrã
Comment[pt_BR]=Barra de menu global no topo da tela
Comment[ru]=Строка меню приложения на верхнем краю экрана
-Comment[sk]=Globálna ponuka na vrchu obrazovky
+Comment[sk]=Globálna ponuka navrchu obrazovky
Comment[sl]=Splošna menijska vrstica na vrhu zaslona
Comment[sr]=Глобална трака менија на врху екрана
Comment[sr@ijekavian]=Глобална трака менија на врху екрана
diff --git a/applets/appmenu/plugin/appmenumodel.cpp b/applets/appmenu/plugin/appmenumodel.cpp
--- a/applets/appmenu/plugin/appmenumodel.cpp
+++ b/applets/appmenu/plugin/appmenumodel.cpp
@@ -175,7 +175,7 @@
if (KWindowSystem::isPlatformX11()) {
auto *c = QX11Info::connection();
- auto getWindowPropertyString = [c, this](WId id, const QByteArray &name) -> QByteArray {
+ auto getWindowPropertyString = [c](WId id, const QByteArray &name) -> QByteArray {
QByteArray value;
if (!s_atoms.contains(name)) {
const xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom(c, false, name.length(), name.constData());
@@ -299,7 +299,7 @@
if (role == MenuRole) { // TODO this should be Qt::DisplayRole
return actions.at(row)->text();
} else if (role == ActionRole) {
- return qVariantFromValue((void *) actions.at(row));
+ return QVariant::fromValue((void *) actions.at(row));
}
return QVariant();
diff --git a/applets/batterymonitor/package/contents/ui/BatteryItem.qml b/applets/batterymonitor/package/contents/ui/BatteryItem.qml
--- a/applets/batterymonitor/package/contents/ui/BatteryItem.qml
+++ b/applets/batterymonitor/package/contents/ui/BatteryItem.qml
@@ -50,7 +50,7 @@
id: brokenBatteryLabel
width: parent ? parent.width : implicitWidth
wrapMode: Text.WordWrap
- text: batteryItem.isBroken && typeof model.Capacity !== "undefined" ? i18n("The capacity of this battery is %1%. This means it is broken and needs a replacement. Please contact your hardware vendor for more details.", model.Capacity) : ""
+ text: batteryItem.isBroken && typeof model.Capacity !== "undefined" ? i18n("This battery's health is at only %1% and should be replaced. Please contact your hardware vendor for more details.", model.Capacity) : ""
font.pointSize: !!detailsLayout.parent.inListView ? theme.smallestFont.pointSize : theme.defaultFont.pointSize
visible: batteryItem.isBroken
}
@@ -209,7 +209,6 @@
visible: !!item
opacity: 0.5
sourceComponent: batteryDetails
- active: batterymonitor.batteries.count < 2
}
}
diff --git a/applets/batterymonitor/package/contents/ui/BrightnessItem.qml b/applets/batterymonitor/package/contents/ui/BrightnessItem.qml
--- a/applets/batterymonitor/package/contents/ui/BrightnessItem.qml
+++ b/applets/batterymonitor/package/contents/ui/BrightnessItem.qml
@@ -23,12 +23,16 @@
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as Components
+import org.kde.plasma.components 3.0 as Components3
RowLayout {
+ id: item
property alias icon: brightnessIcon.source
property alias label: brightnessLabel.text
property alias value: brightnessSlider.value
- property alias maximumValue: brightnessSlider.maximumValue
+ property alias maximumValue: brightnessSlider.to
+ property alias stepSize: brightnessSlider.stepSize
+ signal moved()
spacing: units.gridUnit
@@ -51,13 +55,14 @@
height: paintedHeight
}
- Components.Slider {
+ Components3.Slider {
id: brightnessSlider
width: parent.width
// Don't allow the slider to turn off the screen
// Please see https://git.reviewboard.kde.org/r/122505/ for more information
- minimumValue: maximumValue > 100 ? 1 : 0
+ from: to > 100 ? 1 : 0
stepSize: 1
+ onMoved: item.moved()
}
}
}
diff --git a/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml b/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml
--- a/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml
+++ b/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml
@@ -70,7 +70,7 @@
BadgeOverlay {
anchors.fill: batteryIcon
- text: batteryContainer.hasBattery ? i18nc("battery percentage below battery icon", "%1%", percent) : i18nc("short symbol to signal there is no battery curently available", "-")
+ text: batteryContainer.hasBattery ? i18nc("battery percentage below battery icon", "%1%", percent) : i18nc("short symbol to signal there is no battery currently available", "-")
icon: batteryIcon
visible: plasmoid.configuration.showPercentage
}
diff --git a/applets/batterymonitor/package/contents/ui/PopupDialog.qml b/applets/batterymonitor/package/contents/ui/PopupDialog.qml
--- a/applets/batterymonitor/package/contents/ui/PopupDialog.qml
+++ b/applets/batterymonitor/package/contents/ui/PopupDialog.qml
@@ -38,18 +38,6 @@
signal powermanagementChanged(bool checked)
- Component.onCompleted: {
- // setup handler on slider value manually to avoid change on creation
-
- brightnessSlider.valueChanged.connect(function() {
- batterymonitor.screenBrightness = brightnessSlider.value
- })
-
- keyboardBrightnessSlider.valueChanged.connect(function() {
- batterymonitor.keyboardBrightness = keyboardBrightnessSlider.value
- })
- }
-
Column {
id: settingsColumn
anchors.horizontalCenter: parent.horizontalCenter
@@ -82,6 +70,9 @@
maximumValue: batterymonitor.maximumScreenBrightness
KeyNavigation.tab: keyboardBrightnessSlider
KeyNavigation.backtab: batteryList
+ stepSize: batterymonitor.maximumScreenBrightness/100
+
+ onMoved: batterymonitor.screenBrightness = value
// Manually dragging the slider around breaks the binding
Connections {
@@ -102,6 +93,8 @@
KeyNavigation.tab: pmSwitch
KeyNavigation.backtab: brightnessSlider
+ onMoved: batterymonitor.keyboardBrightness = value
+
// Manually dragging the slider around breaks the binding
Connections {
target: batterymonitor
diff --git a/applets/batterymonitor/package/contents/ui/batterymonitor.qml b/applets/batterymonitor/package/contents/ui/batterymonitor.qml
--- a/applets/batterymonitor/package/contents/ui/batterymonitor.qml
+++ b/applets/batterymonitor/package/contents/ui/batterymonitor.qml
@@ -40,12 +40,9 @@
return PlasmaCore.Types.ActiveStatus
}
- if (pmSource.data.Battery["Has Cumulative"]) {
- if (pmSource.data.Battery.State !== "Charging" && pmSource.data.Battery.Percent <= 5) {
- return PlasmaCore.Types.NeedsAttentionStatus
- } else if (pmSource.data["Battery"]["State"] !== "FullyCharged") {
- return PlasmaCore.Types.ActiveStatus
- }
+ if (pmSource.data.Battery["Has Cumulative"]
+ && pmSource.data["Battery"]["State"] !== "FullyCharged") {
+ return PlasmaCore.Types.ActiveStatus
}
return PlasmaCore.Types.PassiveStatus
@@ -105,6 +102,7 @@
readonly property var kcm_energyinfo: ["kcm_energyinfo.desktop"]
readonly property bool kcmEnergyInformationAuthorized: KCMShell.authorize(kcm_energyinfo).length > 0
+ property QtObject updateScreenBrightnessJob
onScreenBrightnessChanged: {
if (disableBrightnessUpdate) {
return;
@@ -114,17 +112,25 @@
operation.brightness = screenBrightness;
// show OSD only when the plasmoid isn't expanded since the moving slider is feedback enough
operation.silent = plasmoid.expanded
- service.startOperationCall(operation);
+ updateScreenBrightnessJob = service.startOperationCall(operation);
+ updateScreenBrightnessJob.finished.connect(function(job) {
+ Logic.updateBrightness(batterymonitor, pmSource);
+ });
}
+
+ property QtObject updateKeyboardBrightnessJob
onKeyboardBrightnessChanged: {
if (disableBrightnessUpdate) {
return;
}
var service = pmSource.serviceForSource("PowerDevil");
var operation = service.operationDescription("setKeyboardBrightness");
operation.brightness = keyboardBrightness;
operation.silent = plasmoid.expanded
- service.startOperationCall(operation);
+ updateKeyboardBrightnessJob = service.startOperationCall(operation);
+ updateKeyboardBrightnessJob.finished.connect(function(job) {
+ Logic.updateBrightness(batterymonitor, pmSource);
+ });
}
function action_powerdevilkcm() {
diff --git a/applets/batterymonitor/package/contents/ui/logic.js b/applets/batterymonitor/package/contents/ui/logic.js
--- a/applets/batterymonitor/package/contents/ui/logic.js
+++ b/applets/batterymonitor/package/contents/ui/logic.js
@@ -42,27 +42,17 @@
}
if (batteryData["Is Power Supply"] && batteryData["Capacity"] != "" && typeof batteryData["Capacity"] == "number") {
- data.push({label: i18n("Capacity:") })
- data.push({value: i18nc("Placeholder is battery capacity", "%1%", batteryData["Capacity"]) })
- }
-
- // Non-powersupply batteries have a name consisting of the vendor and model already
- if (batteryData["Is Power Supply"]) {
- if (batteryData["Vendor"] != "" && typeof batteryData["Vendor"] == "string") {
- data.push({label: i18n("Vendor:") })
- data.push({value: batteryData["Vendor"] })
- }
-
- if (batteryData["Product"] != "" && typeof batteryData["Product"] == "string") {
- data.push({label: i18n("Model:") })
- data.push({value: batteryData["Product"] })
- }
+ data.push({label: i18n("Battery Health:") })
+ data.push({value: i18nc("Placeholder is battery health percentage", "%1%", batteryData["Capacity"]) })
}
return data
}
function updateBrightness(rootItem, source) {
+ if (rootItem.updateScreenBrightnessJob || rootItem.updateKeyboardBrightnessJob)
+ return;
+
if (!source.data["PowerDevil"]) {
return;
}
diff --git a/applets/batterymonitor/package/metadata.desktop b/applets/batterymonitor/package/metadata.desktop
--- a/applets/batterymonitor/package/metadata.desktop
+++ b/applets/batterymonitor/package/metadata.desktop
@@ -1,8 +1,9 @@
[Desktop Entry]
Name=Battery and Brightness
Name[ar]=البطّاريّة والسّطوع
+Name[ast]=Batería y brilléu
Name[ca]=Bateria i lluminositat
-Name[ca@valencia]=Bateria i lluminositat
+Name[ca@valencia]=Bateria i brillantor
Name[cs]=Baterie a Jas
Name[da]=Batteri og lysstyrke
Name[de]=Akku und Bildschirmhelligkeit
@@ -21,7 +22,8 @@
Name[it]=Batteria e luminosità
Name[ja]=バッテリーと明るさ
Name[ko]=배터리와 밝기
-Name[lt]=Baterija ir ryškumas
+Name[lt]=Akumuliatorius ir ryškumas
+Name[lv]=Baterija un gaišums
Name[nl]=Batterij en helderheid
Name[nn]=Batteri og lysstyrke
Name[pa]=ਬੈਟਰੀ ਤੇ ਚਮਕ
@@ -36,13 +38,15 @@
Name[sr@ijekavianlatin]=Baterija i osvjetljaj
Name[sr@latin]=Baterija i osvetljaj
Name[sv]=Batteri och ljusstyrka
+Name[tg]=Батарея ва дурахшонӣ
Name[tr]=Pil ve Ekran Parlaklığı
Name[uk]=Акумулятор і яскравість дисплея
Name[x-test]=xxBattery and Brightnessxx
Name[zh_CN]=电池和亮度
Name[zh_TW]=電池與亮度
Comment=See the power status of your battery
Comment[ar]=طالع حالة طاقة البطارية
+Comment[ast]=Visualiza l'estáu de la carga de la batería
Comment[be@latin]=Naziraje za kolkaściu enerhii ŭ tvajoj batarei
Comment[bg]=Показва състоянието на батерията
Comment[bs]=Pazite na stanje popunjenosti baterije
@@ -57,7 +61,7 @@
Comment[eo]=Montri energian staton de viaj baterioj
Comment[es]=Mire el estado de potencia de su batería
Comment[et]=Aku oleku näitaja
-Comment[eu]=Ikus bateriaren energia-egoera
+Comment[eu]=Ikusi zure bateriaren energia-egoera
Comment[fi]=Näyttää akun virtatilanteen
Comment[fr]=Voir le niveau d'énergie de votre batterie
Comment[fy]=Besjoch de stroomtastân fan jo batterij
@@ -79,7 +83,7 @@
Comment[km]=មើលស្ថានភាពថាមពលនៃថ្មរបស់អ្នក
Comment[kn]=ನಿನ್ನ ವಿದ್ಯುತ್ಕೋಶದ (ಬಾಟರಿ) ಸ್ಥಿತಿಯನ್ನು ನೋಡು
Comment[ko]=배터리 상태를 표시합니다
-Comment[lt]=Rodyti akumuliatoriaus įkrovimą
+Comment[lt]=Rodyti akumuliatoriaus įkrovą
Comment[lv]=Rāda baterijas izlādes stāvokli
Comment[mk]=Видете го енергетскиот статус на вашата батерија
Comment[ml]=നിങ്ങളുടെ ബാറ്ററിയുടെ വൈദ്യുതി ഊര്ജ്ജ സ്ഥിതി കാണുക
@@ -104,7 +108,7 @@
Comment[sv]=Se batteriets laddningsstatus
Comment[ta]=See the power status of your battery
Comment[te]=మీ బ్యాటరీ యొక్క పవర్ స్థితిని చూడుము
-Comment[tg]=Намоиши ҳолати барқи батарея
+Comment[tg]=Вазъияти қувваи барқи батареяро назорат кунед
Comment[th]=แสดงสถานะพลังงานของแบตเตอรี่ของคุณ
Comment[tr]=Pilinizin güç durumuna bakın
Comment[ug]=توكدانىڭىزنىڭ توك ھالىتىنى كۆرسىتىدۇ
@@ -115,6 +119,7 @@
Comment[zh_TW]=查看您的電池的電力狀態
Keywords=Power Management;Battery;System;Energy;
Keywords[ar]=إدارة الطاقة;بطارية;نظام;طاقة;
+Keywords[ast]=Xestión d'enerxía;Batería;Sistema;Enerxía;
Keywords[bs]=Upravljanje napajenjem;Baterija;Sistem;Energija;
Keywords[ca]=Sistema de gestió d'energia;Bateria;Sistema;Energia;
Keywords[ca@valencia]=Sistema de gestió d'energia;Bateria;Sistema;Energia;
@@ -138,7 +143,7 @@
Keywords[ja]=電源管理;バッテリー;システム;エネルギー;
Keywords[kk]=Power Management;Battery;System;Energy;
Keywords[ko]=Power Management;Battery;System;Energy;전원 관리;전력 관리;배터리;시스템;에너지;
-Keywords[lt]=Energijos valdymas;Baterija;Sistema;Energija;Akumuliatorius;
+Keywords[lt]=Energijos valdymas;Baterija;Sistema;Energija;Akumuliatorius;Maitinimo valdymas
Keywords[mr]=वीज व्यवस्थापन; बॅटरी; प्रणाली; ऊर्जा;
Keywords[nb]=Strømstyring; Batteri; System; Energi;
Keywords[nds]=Stroompleeg,Batterie,Systeem,Energie
@@ -163,7 +168,7 @@
Keywords[zh_CN]=电源管理;电池;系统;能源;
Keywords[zh_TW]=Power Management;Battery;System;Energy;
-Icon=battery
+Icon=battery-full
Type=Service
X-KDE-ServiceTypes=Plasma/Applet
@@ -180,7 +185,7 @@
X-KDE-PluginInfo-License=GPL-2.0+
X-KDE-PluginInfo-Name=org.kde.plasma.battery
X-KDE-PluginInfo-Version=3.0
-X-KDE-PluginInfo-Website=http://vizZzion.org
+X-KDE-PluginInfo-Website=https://vizzzion.org
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-EnabledByDefault=true
X-Plasma-DBusActivationService=org.kde.Solid.PowerManagement
diff --git a/applets/calendar/package/metadata.desktop b/applets/calendar/package/metadata.desktop
--- a/applets/calendar/package/metadata.desktop
+++ b/applets/calendar/package/metadata.desktop
@@ -1,6 +1,7 @@
[Desktop Entry]
Name=Calendar
Name[ar]=التقويم
+Name[ast]=Calendariu
Name[be@latin]=Kalandar
Name[bg]=Календар
Name[bn]=ক্যালেণ্ডার
@@ -85,6 +86,7 @@
Comment[el]=Εμφάνιση συναντήσεων και συμβάντων ανά μήνα
Comment[en_GB]=Month display with your appointments and events
Comment[es]=Calendario mensual con sus citas y eventos
+Comment[et]=Kuuvaade ühes kõigi kohtumiste ja sündmustega
Comment[eu]=Hileko ikuspegia zure hitzordu eta gertaerekin
Comment[fi]=Kalenterimerkintöjesi kuukausinäkymä
Comment[fr]=Vue mensuelle avec vos rendez-vous et évènements
diff --git a/applets/clipboard/contents/ui/ClipboardPage.qml b/applets/clipboard/contents/ui/ClipboardPage.qml
--- a/applets/clipboard/contents/ui/ClipboardPage.qml
+++ b/applets/clipboard/contents/ui/ClipboardPage.qml
@@ -82,7 +82,7 @@
level: 3
opacity: 0.6
visible: clipboardMenu.model.count === 0 && filter.length === 0
- text: i18n("Clipboard history is empty.")
+ text: i18n("Clipboard is empty")
}
RowLayout {
diff --git a/applets/clipboard/contents/ui/ImageItemDelegate.qml b/applets/clipboard/contents/ui/ImageItemDelegate.qml
--- a/applets/clipboard/contents/ui/ImageItemDelegate.qml
+++ b/applets/clipboard/contents/ui/ImageItemDelegate.qml
@@ -24,7 +24,8 @@
KQuickControlsAddons.QPixmapItem {
id: previewPixmap
- height: Math.round(width * (nativeHeight/nativeWidth) + units.smallSpacing * 2)
+ width: Math.min(nativeWidth, width)
+ height: Math.min(nativeHeight, Math.round(width * (nativeHeight/nativeWidth) + units.smallSpacing * 2))
pixmap: DecorationRole
fillMode: KQuickControlsAddons.QPixmapItem.PreserveAspectFit
}
diff --git a/applets/clipboard/metadata.desktop b/applets/clipboard/metadata.desktop
--- a/applets/clipboard/metadata.desktop
+++ b/applets/clipboard/metadata.desktop
@@ -1,6 +1,7 @@
[Desktop Entry]
Name=Clipboard
Name[ar]=الحافظة
+Name[ast]=Cartafueyu
Name[bs]=Klipbord
Name[ca]=Porta-retalls
Name[ca@valencia]=Porta-retalls
@@ -23,6 +24,7 @@
Name[ja]=クリップボード
Name[ko]=클립보드
Name[lt]=Iškarpinė
+Name[lv]=Starpliktuve
Name[nb]=Utklippstavle
Name[nds]=Twischenaflaag
Name[nl]=Klembord
@@ -39,6 +41,7 @@
Name[sr@ijekavianlatin]=klipbord
Name[sr@latin]=klipbord
Name[sv]=Klippbord
+Name[tg]=Ҳофизаи муваққатӣ
Name[tr]=Pano
Name[uk]=Буфер обміну
Name[x-test]=xxClipboardxx
@@ -54,14 +57,16 @@
Comment[el]=Παρέχει πρόσβαση στο ιστορικό του προχείρου
Comment[en_GB]=Provides access to the clipboard history
Comment[es]=Proporciona acceso al historial del portapapeles
+Comment[et]=Lõikepuhvri ajaloo kasutamine
Comment[eu]=Arbeleko historiara sarbidea ematen du
Comment[fi]=Tarjoaa pääsyn leikepöydän historiaan
Comment[fr]=Donne accès à l'historique du presse-papiers
Comment[gl]=Fornece acceso ao historial do portapapeis.
Comment[hu]=Hozzáférést nyújt a vágólap előzményeihez
Comment[id]=Menyediakan akses ke histori papan-klip
Comment[it]=Fornisce l'accesso alla cronologia degli appunti
Comment[ko]=클립보드 기록 표시
+Comment[lt]=Suteikia prieigą prie iškarpinės istorijos
Comment[nl]=Biedt toegang tot de geschiedenis van klembord
Comment[nn]=Gjev tilgang til utklippstavleloggen
Comment[pa]=ਕਲਿੱਪਬੋਰਡ ਅਤੀਤ ਲਈ ਪਹੁੰਚ ਦਿੰਦਾ ਹੈ
diff --git a/applets/devicenotifier/package/contents/ui/devicenotifier.qml b/applets/devicenotifier/package/contents/ui/devicenotifier.qml
--- a/applets/devicenotifier/package/contents/ui/devicenotifier.qml
+++ b/applets/devicenotifier/package/contents/ui/devicenotifier.qml
@@ -100,8 +100,11 @@
source: devicenotifier.popupIcon
width: units.iconSizes.medium;
height: units.iconSizes.medium;
+ active: compactMouse.containsMouse
MouseArea {
+ id: compactMouse
anchors.fill: parent
+ hoverEnabled: true
onClicked: plasmoid.expanded = !plasmoid.expanded
}
}
diff --git a/applets/devicenotifier/package/metadata.desktop b/applets/devicenotifier/package/metadata.desktop
--- a/applets/devicenotifier/package/metadata.desktop
+++ b/applets/devicenotifier/package/metadata.desktop
@@ -1,6 +1,7 @@
[Desktop Entry]
Name=Device Notifier
Name[ar]=مُخطِر الأجهزة
+Name[ast]=Avisador de preseos
Name[be@latin]=Infarmavańnie pra novyja pryłady
Name[bg]=Уведомяване за нови устройства
Name[bn]=ডিভাইস নোটিফায়ার
@@ -65,7 +66,6 @@
Name[sr@latin]=izveštavač o uređajima
Name[sv]=Underrättelse om enheter
Name[ta]=கருவி நோட்டம்
-Name[tg]=Извещение о новых устройствах
Name[th]=แจ้งให้ทราบถึงอุปกรณ์
Name[tr]=Aygıt Bildirici
Name[ug]=ئۈسكۈنە ئۇقتۇرۇشى
@@ -91,7 +91,7 @@
Comment[eo]=Atentigoj kaj atingo por novaj aparatoj
Comment[es]=Notificaciones y accesos de nuevos dispositivos
Comment[et]=Uutest seadmetest teatamine ja nende kasutamine
-Comment[eu]=Gailu berrien jakinarazpenak eta sarbideak
+Comment[eu]=Jakinarazpenak eta gailu berrietara sarbidea
Comment[fi]=Ilmoitukset ja pääsy uusille laitteille
Comment[fr]=Notifications et accès aux nouveaux périphériques
Comment[fy]=Notifikaasjes en tagong foar nije apparaten
@@ -114,7 +114,7 @@
Comment[kn]=ಹೊಸ ಸಾಧನಗಳಿಗೆ ಸೂಚನೆಗಳು ಮತ್ತು ನಿಲುಕಣೆ
Comment[ko]=새 장치가 연결된 것을 알려 주고 접근할 수 있도록 합니다
Comment[ku]=Hişyarî û gihîştin ji bo cîhazên nû
-Comment[lt]=Pranešimų apie įrenginius ir jų atvėrimo priemonė
+Comment[lt]=Pranešimų apie įrenginius ir prieiga prie jų
Comment[lv]=Paziņojumi un piekļuve jaunām ierīcēm
Comment[mk]=Известувања и пристап до нови уреди
Comment[ml]=പുതിയ ഉപകരണങ്ങളുടെ അറിയിപ്പുകളും സമീപനവും
@@ -139,7 +139,6 @@
Comment[sv]=Underrättelser och åtkomst av nya enheter
Comment[ta]=Notifications and access for new devices
Comment[te]=కొత్త పరికరముల కొరకు నోటీసులు మరియు యాక్సెస్
-Comment[tg]=Огоҳиҳо ва дастрасӣ ба дастгоҳҳои нав
Comment[th]=การแจ้งให้ทราบและเข้าใช้งานอุปกรณ์ใหม่
Comment[tr]=Yeni donanım bildirimleri ve yeni donanımlara erişim
Comment[ug]=يېڭى ئۈسكۈنە ئۇقتۇرۇشى ۋە زىيارىتى
@@ -164,7 +163,7 @@
X-KDE-PluginInfo-Email=wilderkde@gmail.com
X-KDE-PluginInfo-Name=org.kde.plasma.devicenotifier
X-KDE-PluginInfo-Version=1.0
-X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/DeviceNotifier
+X-KDE-PluginInfo-Website=https://userbase.kde.org/Plasma/DeviceNotifier
X-KDE-PluginInfo-Category=System Information
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL-2.0+
diff --git a/applets/devicenotifier/test-predicate-openinwindow.desktop b/applets/devicenotifier/test-predicate-openinwindow.desktop
--- a/applets/devicenotifier/test-predicate-openinwindow.desktop
+++ b/applets/devicenotifier/test-predicate-openinwindow.desktop
@@ -38,8 +38,8 @@
Name[km]=បើកជាមួយកម្មវិធីគ្រប់គ្រងឯកសារ
Name[kn]=ಕಡತ ವ್ಯವಸ್ಥಾಪಕದೊಂದಿಗೆ ತೆರೆ
Name[ko]=파일 관리자로 열기
-Name[lt]=Atverti su failų tvarkykle
-Name[lv]=Atvērt failu pārvaldniekā
+Name[lt]=Atverti naudojant failų tvarkytuvę
+Name[lv]=Atvērt datņu pārvaldniekā
Name[mk]=Отвори со менаџер на датотеки
Name[ml]=ഫയലുകളുടെ നടത്തിപ്പുകാരനില് തുറക്കുക
Name[mr]=फाईल व्यवस्थापकात उघडा
@@ -61,7 +61,7 @@
Name[sr@ijekavianlatin]=Otvori menadžerom fajlova
Name[sr@latin]=Otvori menadžerom fajlova
Name[sv]=Öppna med filhanterare
-Name[tg]=Кушодан бо намоишгари Интернети &стандартӣ
+Name[tg]=Кушодан ба воситаи мудири файлҳо
Name[th]=เปิดใช้งานผ่านเครื่องมือจัดการแฟ้ม
Name[tr]=Dosya yöneticisi ile aç
Name[ug]=ھۆججەت باشقۇرغۇدا ئاچ
diff --git a/applets/digital-clock/package/contents/config/config.qml b/applets/digital-clock/package/contents/config/config.qml
--- a/applets/digital-clock/package/contents/config/config.qml
+++ b/applets/digital-clock/package/contents/config/config.qml
@@ -35,7 +35,7 @@
}
ConfigCategory {
name: i18n("Calendar")
- icon: "view-calendar"
+ icon: "office-calendar"
source: "configCalendar.qml"
}
ConfigCategory {
diff --git a/applets/digital-clock/package/contents/ui/CalendarView.qml b/applets/digital-clock/package/contents/ui/CalendarView.qml
--- a/applets/digital-clock/package/contents/ui/CalendarView.qml
+++ b/applets/digital-clock/package/contents/ui/CalendarView.qml
@@ -33,6 +33,8 @@
property int _minimumHeight: units.gridUnit * 14
Layout.preferredWidth: _minimumWidth
Layout.preferredHeight: _minimumHeight * 1.5
+ Layout.maximumWidth: Layout.preferredWidth
+ Layout.maximumHeight: Layout.preferredHeight
readonly property bool showAgenda: PlasmaCalendar.EventPluginsManager.enabledPlugins.length > 0
diff --git a/applets/digital-clock/package/contents/ui/DigitalClock.qml b/applets/digital-clock/package/contents/ui/DigitalClock.qml
--- a/applets/digital-clock/package/contents/ui/DigitalClock.qml
+++ b/applets/digital-clock/package/contents/ui/DigitalClock.qml
@@ -301,13 +301,13 @@
PropertyChanges {
target: dateLabel
- // this can be marginal bigger than contentHeight because of the horizontal fit
- height: sizehelper.contentHeight
+ height: dateLabel.paintedHeight
width: main.width
fontSizeMode: Text.Fit
minimumPixelSize: Math.min(0.7 * theme.smallestFont.pixelSize, timeLabel.height)
elide: Text.ElideRight
+ wrapMode: Text.WordWrap
}
AnchorChanges {
@@ -427,7 +427,7 @@
var newIndex = main.tzIndex;
wheelDelta += delta;
// magic number 120 for common "one click"
- // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop
+ // See: https://doc.qt.io/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop
while (wheelDelta >= 120) {
wheelDelta -= 120;
newIndex--;
diff --git a/applets/digital-clock/package/contents/ui/configAppearance.qml b/applets/digital-clock/package/contents/ui/configAppearance.qml
--- a/applets/digital-clock/package/contents/ui/configAppearance.qml
+++ b/applets/digital-clock/package/contents/ui/configAppearance.qml
@@ -29,8 +29,6 @@
QtLayouts.ColumnLayout {
id: appearancePage
- width: childrenRect.width
- height: childrenRect.height
signal configurationChanged
@@ -143,48 +141,63 @@
Kirigami.FormData.isSection: true
}
- QtControls.ComboBox {
- id: dateFormat
+ QtLayouts.RowLayout {
Kirigami.FormData.label: i18n("Date format:")
enabled: showDate.checked
- textRole: "label"
- model: [
- {
- 'label': i18n("Long Date"),
- 'name': "longDate"
- },
- {
- 'label': i18n("Short Date"),
- 'name': "shortDate"
- },
- {
- 'label': i18n("ISO Date"),
- 'name': "isoDate"
- },
- {
- 'label': i18nc("custom date format", "Custom"),
- 'name': "custom"
- }
- ]
- onCurrentIndexChanged: cfg_dateFormat = model[currentIndex]["name"]
- Component.onCompleted: {
- for (var i = 0; i < model.length; i++) {
- if (model[i]["name"] === plasmoid.configuration.dateFormat) {
- dateFormat.currentIndex = i;
+ QtControls.ComboBox {
+ id: dateFormat
+ textRole: "label"
+ model: [
+ {
+ 'label': i18n("Long Date"),
+ 'name': "longDate",
+ format: Qt.SystemLocaleLongDate
+ },
+ {
+ 'label': i18n("Short Date"),
+ 'name': "shortDate",
+ format: Qt.SystemLocaleShortDate
+ },
+ {
+ 'label': i18n("ISO Date"),
+ 'name': "isoDate",
+ format: Qt.ISODate
+ },
+ {
+ 'label': i18nc("custom date format", "Custom"),
+ 'name': "custom"
+ }
+ ]
+ onCurrentIndexChanged: cfg_dateFormat = model[currentIndex]["name"]
+
+ Component.onCompleted: {
+ for (var i = 0; i < model.length; i++) {
+ if (model[i]["name"] === plasmoid.configuration.dateFormat) {
+ dateFormat.currentIndex = i;
+ }
}
}
}
+
+ QtControls.Label {
+ QtLayouts.Layout.fillWidth: true
+ textFormat: Text.PlainText
+ text: Qt.formatDate(new Date(), cfg_dateFormat === "custom" ? customDateFormat.text
+ : dateFormat.model[dateFormat.currentIndex].format)
+ }
}
QtControls.TextField {
id: customDateFormat
QtLayouts.Layout.fillWidth: true
+ enabled: showDate.checked
visible: cfg_dateFormat == "custom"
}
QtControls.Label {
- text: i18n("Time Format Documentation")
+ text: i18n("Time Format Documentation")
+ enabled: showDate.checked
visible: cfg_dateFormat == "custom"
wrapMode: Text.Wrap
QtLayouts.Layout.preferredWidth: QtLayouts.Layout.maximumWidth
@@ -233,7 +246,7 @@
}
icon.name: "format-text-bold"
checkable: true
- Accessible.name: tooltip
+ Accessible.name: QtControls.ToolTip.text
}
QtControls.Button {
@@ -243,7 +256,7 @@
}
icon.name: "format-text-italic"
checkable: true
- Accessible.name: tooltip
+ Accessible.name: QtControls.ToolTip.text
}
}
}
diff --git a/applets/digital-clock/package/contents/ui/configTimeZones.qml b/applets/digital-clock/package/contents/ui/configTimeZones.qml
--- a/applets/digital-clock/package/contents/ui/configTimeZones.qml
+++ b/applets/digital-clock/package/contents/ui/configTimeZones.qml
@@ -18,19 +18,17 @@
* along with this program. If not, see
*/
-import QtQuick 2.0
-import QtQuick.Controls 1.2 as QtControls
+import QtQuick 2.13
+import QtQuick.Controls 2.8 as QQC2
import QtQuick.Layouts 1.0
import QtQuick.Dialogs 1.1
import org.kde.plasma.private.digitalclock 1.0
import org.kde.plasma.core 2.0 as PlasmaCore
-import org.kde.plasma.components 2.0 as PlasmaComponents
+import org.kde.kirigami 2.5 as Kirigami
-Item {
+ColumnLayout {
id: timeZonesPage
- width: parent.width
- height: parent.height
property alias cfg_selectedTimeZones: timeZones.selectedTimeZones
property alias cfg_wheelChangesTimezone: enableWheelCheckBox.checked
@@ -47,163 +45,63 @@
}
}
- // This is just for getting the column width
- QtControls.CheckBox {
- id: checkbox
- visible: false
+ Kirigami.InlineMessage {
+ id: messageWidget
+ Layout.fillWidth: true
+ Layout.margins: Kirigami.Units.smallSpacing
+ type: Kirigami.MessageType.Warning
+ text: i18n("At least one time zone needs to be enabled. 'Local' was enabled automatically.")
+ showCloseButton: true
}
+ QQC2.TextField {
+ id: filter
+ Layout.fillWidth: true
+ placeholderText: i18n("Search Time Zones")
+ }
- ColumnLayout {
- anchors.fill: parent
-
- Rectangle {
- id: messageWidget
-
- anchors {
- left: parent.left
- right: parent.right
- margins: 1
- }
-
- height: 0
-
- //TODO: This is the actual color KMessageWidget uses as its base color but here it gives
- // a different color, figure out why
- //property color gradBaseColor: Qt.rgba(0.69, 0.5, 0, 1)
- gradient: Gradient {
- GradientStop { position: 0.0; color: "#FFD86D" } //Qt.lighter(messageWidget.gradBaseColor, 1.1)
- GradientStop { position: 0.1; color: "#EAC360" } // messageWidget.gradBaseColor
- GradientStop { position: 1.0; color: "#CAB064" } //Qt.darker(messageWidget.gradBaseColor, 1.1)
- }
-
- radius: 5
- border.width: 1
- border.color: "#79735B"
-
- visible: false
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
- Behavior on visible {
- ParallelAnimation {
- PropertyAnimation {
- target: messageWidget
- property: "opacity"
- to: messageWidget.visible ? 0 : 1.0
- easing.type: Easing.Linear
- }
- PropertyAnimation {
- target: messageWidget
- property: "Layout.minimumHeight"
- to: messageWidget.visible ? 0 : messageWidgetLabel.height + (2 *units.largeSpacing)
- easing.type: Easing.Linear
- }
- }
- }
+ QQC2.ScrollView {
+ anchors.fill: parent
+ clip: true
+ Component.onCompleted: background.visible = true // enable border
- RowLayout {
- anchors.fill: parent
- anchors.margins: units.largeSpacing
- anchors.leftMargin: units.smallSpacing
- anchors.rightMargin: units.smallSpacing
- spacing: units.smallSpacing
-
- PlasmaCore.IconItem {
- anchors.verticalCenter: parent.verticalCenter
- height: units.iconSizes.smallMedium
- width: height
- source: "dialog-warning"
- }
+ ListView {
+ id: listView
+ focus: true // keyboard navigation
+ activeFocusOnTab: true // keyboard navigation
- QtControls.Label {
- id: messageWidgetLabel
- anchors.verticalCenter: parent.verticalCenter
- Layout.fillWidth: true
- text: i18n("At least one time zone needs to be enabled. 'Local' was enabled automatically.")
- verticalAlignment: Text.AlignVCenter
- wrapMode: Text.WordWrap
+ model: TimeZoneFilterProxy {
+ sourceModel: timeZones
+ filterString: filter.text
}
- PlasmaComponents.ToolButton {
- anchors.verticalCenter: parent.verticalCenter
- iconName: "dialog-close"
- flat: true
-
- onClicked: {
- messageWidget.visible = false;
+ delegate: QQC2.CheckDelegate {
+ id: checkbox
+ focus: true // keyboard navigation
+ width: parent.width
+ text: !city || city.indexOf("UTC") === 0 ? comment : comment ? i18n("%1, %2 (%3)", city, region, comment) : i18n("%1, %2", city, region)
+ checked: model.checked
+ onToggled: {
+ model.checked = checkbox.checked
+ listView.currentIndex = index // highlight
+ listView.forceActiveFocus() // keyboard navigation
}
+ highlighted: ListView.isCurrentItem
}
}
}
+ }
- QtControls.TextField {
- id: filter
- Layout.fillWidth: true
- placeholderText: i18n("Search Time Zones")
- }
-
- QtControls.TableView {
- id: timeZoneView
-
- signal toggleCurrent
-
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- Keys.onSpacePressed: toggleCurrent()
-
- model: TimeZoneFilterProxy {
- sourceModel: timeZones
- filterString: filter.text
- }
-
- QtControls.TableViewColumn {
- role: "checked"
- width: checkbox.width
- delegate:
- QtControls.CheckBox {
- id: checkBox
- anchors.centerIn: parent
- checked: styleData.value
- activeFocusOnTab: false // only let the TableView as a whole get focus
- onClicked: {
- //needed for model's setData to be called
- model.checked = checked;
- }
-
- Connections {
- target: timeZoneView
- onToggleCurrent: {
- if (styleData.row === timeZoneView.currentRow) {
- model.checked = !checkBox.checked
- }
- }
- }
- }
-
- resizable: false
- movable: false
- }
- QtControls.TableViewColumn {
- role: "city"
- title: i18n("City")
- }
- QtControls.TableViewColumn {
- role: "region"
- title: i18n("Region")
- }
- QtControls.TableViewColumn {
- role: "comment"
- title: i18n("Comment")
- }
- }
-
- RowLayout {
- Layout.fillWidth: true
- QtControls.CheckBox {
- id: enableWheelCheckBox
- text: i18n("Switch time zone with mouse wheel")
- }
+ RowLayout {
+ Layout.fillWidth: true
+ QQC2.CheckBox {
+ id: enableWheelCheckBox
+ text: i18n("Switch time zone with mouse wheel")
}
-
}
+
}
diff --git a/applets/digital-clock/package/contents/ui/main.qml b/applets/digital-clock/package/contents/ui/main.qml
--- a/applets/digital-clock/package/contents/ui/main.qml
+++ b/applets/digital-clock/package/contents/ui/main.qml
@@ -32,6 +32,7 @@
width: units.gridUnit * 10
height: units.gridUnit * 4
property string dateFormatString: setDateFormatString()
+ Plasmoid.backgroundHints: PlasmaCore.Types.ShadowBackground | PlasmaCore.Types.ConfigurableBackground
property date tzDate: {
// get the time for the given timezone from the dataengine
var now = dataSource.data[plasmoid.configuration.lastSelectedTimezone]["DateTime"];
diff --git a/applets/digital-clock/package/metadata.desktop b/applets/digital-clock/package/metadata.desktop
--- a/applets/digital-clock/package/metadata.desktop
+++ b/applets/digital-clock/package/metadata.desktop
@@ -2,6 +2,7 @@
Name=Digital Clock
Name[af]=Digitale horlosie
Name[ar]=ساعة رقمية
+Name[ast]=Reló dixital
Name[be]=Лічбавы гадзіннік
Name[be@latin]=Ličbavy hadzińnik
Name[bg]=Цифров часовник
@@ -97,7 +98,7 @@
Comment[en_GB]=Time displayed in a digital format
Comment[es]=La hora mostrada en formato digital
Comment[et]=Aja esitamine digitaalsel kujul
-Comment[eu]=Ordua formatu digitalean bistaratua
+Comment[eu]=Ordua formatu digitalean azaldua
Comment[fi]=Aika näytettynä digitaalimuodossa
Comment[fr]=L'heure au format numérique
Comment[gl]=A hora mostrada nun formato dixital
diff --git a/applets/digital-clock/plugin/CMakeLists.txt b/applets/digital-clock/plugin/CMakeLists.txt
--- a/applets/digital-clock/plugin/CMakeLists.txt
+++ b/applets/digital-clock/plugin/CMakeLists.txt
@@ -2,7 +2,7 @@
find_package(IsoCodes)
set_package_properties(IsoCodes PROPERTIES DESCRIPTION "ISO language, territory, currency, script codes and their translations"
- URL "http://pkg-isocodes.alioth.debian.org/"
+ URL "https://salsa.debian.org/iso-codes-team/iso-codes"
PURPOSE "Translation of country names in digital clock applet"
TYPE RUNTIME
)
diff --git a/applets/digital-clock/plugin/clipboardmenu.cpp b/applets/digital-clock/plugin/clipboardmenu.cpp
--- a/applets/digital-clock/plugin/clipboardmenu.cpp
+++ b/applets/digital-clock/plugin/clipboardmenu.cpp
@@ -97,7 +97,7 @@
a->setData(s);
if (m_secondsIncluded) {
s = time.toString(Qt::SystemLocaleLongDate);
- s.replace(rx, QString());
+ s.remove(rx);
a = menu->addAction(s);
a->setData(s);
s = time.toString(Qt::SystemLocaleLongDate);
diff --git a/applets/icon/iconapplet.cpp b/applets/icon/iconapplet.cpp
--- a/applets/icon/iconapplet.cpp
+++ b/applets/icon/iconapplet.cpp
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -110,7 +111,7 @@
connect(statJob, &KIO::StatJob::finished, this, [=] {
QString desiredDesktopFileName = m_url.fileName();
- // in doubt, just encode the entire URL, e.g. http://www.kde.org/ has no filename
+ // in doubt, just encode the entire URL, e.g. https://www.kde.org/ has no filename
if (desiredDesktopFileName.isEmpty()) {
desiredDesktopFileName = KIO::encodeFileName(m_url.toDisplayString());
}
@@ -121,9 +122,9 @@
}
QString backingDesktopFile = plasmaIconsFolderPath + QLatin1Char('/');
- // KIO::suggestName always appends a suffix, i.e. it expects that we already know the file already exists
+ // KFileUtils::suggestName always appends a suffix, i.e. it expects that we already know the file already exists
if (QFileInfo::exists(backingDesktopFile + desiredDesktopFileName)) {
- desiredDesktopFileName = KIO::suggestName(QUrl::fromLocalFile(plasmaIconsFolderPath), desiredDesktopFileName);
+ desiredDesktopFileName = KFileUtils::suggestName(QUrl::fromLocalFile(plasmaIconsFolderPath), desiredDesktopFileName);
}
backingDesktopFile.append(desiredDesktopFileName);
@@ -181,7 +182,7 @@
}
// KFileItem might return "." as text for e.g. root folders
- if (name == QLatin1String(".")) {
+ if (name == QLatin1Char('.')) {
name.clear();
}
@@ -387,7 +388,7 @@
if (!m_openContainingFolderAction) {
if (KProtocolManager::supportsListing(linkUrl)) {
m_openContainingFolderAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("Open Containing Folder"), this);
- connect(m_openContainingFolderAction, &QAction::triggered, this, [this, linkUrl] {
+ connect(m_openContainingFolderAction, &QAction::triggered, this, [ linkUrl] {
KIO::highlightInFileManager({linkUrl});
});
}
diff --git a/applets/icon/package/metadata.desktop b/applets/icon/package/metadata.desktop
--- a/applets/icon/package/metadata.desktop
+++ b/applets/icon/package/metadata.desktop
@@ -3,6 +3,7 @@
Name[af]=Ikoon
Name[ar]=أيقونة
Name[as]=আইকন
+Name[ast]=Iconu
Name[be@latin]=Ikona
Name[bg]=Икона
Name[bn]=আইকন
@@ -43,7 +44,7 @@
Name[kn]=ಚಿಹ್ನೆ
Name[ko]=아이콘
Name[ku]=Îkon
-Name[lt]=Ženkliukas
+Name[lt]=Piktograma
Name[lv]=Ikona
Name[mai]=चिह्न
Name[mk]=Икона
@@ -86,6 +87,7 @@
Name[zh_TW]=圖示
Comment=A generic icon
Comment[ar]=أيقونة عامّة
+Comment[ast]=Un iconu xenéricu
Comment[be@latin]=Prostaja ikona
Comment[bg]=Обикновена икона
Comment[bn_IN]=একটি সাধারণ আইকন
@@ -124,7 +126,7 @@
Comment[kn]=ಒಂದು ಅಲಿಪ್ತ (ಜೆನೆರಿಕ್) ಚಿಹ್ನೆ
Comment[ko]=일반적인 아이콘
Comment[ku]=Îkoneke jenerîk
-Comment[lt]=Bendro pobūdžio ženkliukas
+Comment[lt]=Bendrinė piktograma
Comment[lv]=Vispārīga ikona
Comment[mk]=Општа икона
Comment[ml]=സാധാരണ പ്രതിരൂപം
@@ -149,7 +151,6 @@
Comment[sr@latin]=Generička ikonica
Comment[sv]=En generell ikon
Comment[ta]=பொதுவான ஓவம்
-Comment[tg]=Нишонаи умумӣ
Comment[th]=ภาพไอคอนทั่วไป
Comment[tr]=Bir genel simge
Comment[ug]=ئادەتتىكى سىنبەلگە
diff --git a/applets/kicker/CMakeLists.txt b/applets/kicker/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/applets/kicker/CMakeLists.txt
@@ -0,0 +1,88 @@
+add_definitions(
+ -DQT_USE_QSTRINGBUILDER
+ -DQT_NO_CAST_TO_ASCII
+# -DQT_NO_CAST_FROM_ASCII
+ -DQT_STRICT_ITERATORS
+ -DQT_NO_URL_CAST_FROM_STRING
+ -DQT_NO_CAST_FROM_BYTEARRAY
+ -DQT_NO_SIGNALS_SLOTS_KEYWORDS
+ -DQT_USE_FAST_OPERATOR_PLUS
+ -DTRANSLATION_DOMAIN=\"libkicker\"
+)
+
+set(kickerplugin_SRCS
+ plugin/abstractentry.cpp
+ plugin/abstractmodel.cpp
+ plugin/actionlist.cpp
+ plugin/appentry.cpp
+ plugin/appsmodel.cpp
+ plugin/computermodel.cpp
+ plugin/contactentry.cpp
+ plugin/containmentinterface.cpp
+ plugin/draghelper.cpp
+ plugin/simplefavoritesmodel.cpp
+ plugin/kastatsfavoritesmodel.cpp
+ plugin/fileentry.cpp
+ plugin/forwardingmodel.cpp
+ plugin/placeholdermodel.cpp
+ plugin/funnelmodel.cpp
+ plugin/dashboardwindow.cpp
+ plugin/kickerplugin.cpp
+ plugin/menuentryeditor.cpp
+ plugin/processrunner.cpp
+ plugin/rootmodel.cpp
+ plugin/runnermodel.cpp
+ plugin/runnermatchesmodel.cpp
+ plugin/recentcontactsmodel.cpp
+ plugin/recentusagemodel.cpp
+ plugin/submenu.cpp
+ plugin/systementry.cpp
+ plugin/systemmodel.cpp
+ plugin/systemsettings.cpp
+ plugin/wheelinterceptor.cpp
+ plugin/windowsystem.cpp
+ plugin/funnelmodel.cpp
+)
+
+ecm_qt_declare_logging_category(kickerplugin_SRCS
+ HEADER debug.h
+ IDENTIFIER KICKER_DEBUG
+ CATEGORY_NAME org.kde.plasma.kicker)
+
+qt5_add_dbus_interface(kickerplugin_SRCS ${CMAKE_SOURCE_DIR}/krunner/dbus/org.kde.krunner.App.xml krunner_interface)
+qt5_add_dbus_interface(kickerplugin_SRCS ${CMAKE_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml ksmserver_interface)
+
+install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/kicker)
+
+add_library(kickerplugin SHARED ${kickerplugin_SRCS})
+
+target_link_libraries(kickerplugin
+ Qt5::Core
+ Qt5::DBus
+ Qt5::Qml
+ Qt5::Quick
+ Qt5::X11Extras
+ KF5::Activities
+ KF5::ActivitiesStats
+ KF5::ConfigCore
+ KF5::CoreAddons
+ KF5::I18n
+ KF5::ItemModels
+ KF5::KDELibs4Support # FIXME: New Solid power management API doesn't exist yet, so we need to use deprecated stuff.
+ KF5::KIOCore
+ KF5::KIOWidgets
+ KF5::KIOFileWidgets
+ KF5::People
+ KF5::PeopleWidgets
+ KF5::PlasmaQuick
+ KF5::Runner
+ KF5::Service
+ KF5::Solid
+ KF5::WindowSystem
+ PW::KWorkspace)
+
+if (${HAVE_APPSTREAMQT})
+target_link_libraries(kickerplugin AppStreamQt)
+endif()
+
+install(TARGETS kickerplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/kicker)
diff --git a/applets/kicker/Messages.sh b/applets/kicker/Messages.sh
new file mode 100644
--- /dev/null
+++ b/applets/kicker/Messages.sh
@@ -0,0 +1,2 @@
+#! /usr/bin/env bash
+$XGETTEXT `find . -name \*.cpp` -o $podir/libkicker.pot
diff --git a/applets/kicker/plugin/abstractentry.h b/applets/kicker/plugin/abstractentry.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/abstractentry.h
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2012 Aurélien Gâteau *
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef ABSTRACTENTRY_H
+#define ABSTRACTENTRY_H
+
+#include "abstractmodel.h"
+
+#include
+#include
+
+class AbstractEntry
+{
+ public:
+ explicit AbstractEntry(AbstractModel *owner);
+ virtual ~AbstractEntry();
+
+ enum EntryType { RunnableType, GroupType, SeparatorType };
+
+ virtual EntryType type() const = 0;
+
+ AbstractModel *owner() const;
+
+ virtual bool isValid() const;
+
+ virtual QIcon icon() const;
+ virtual QString name() const;
+ virtual QString group() const;
+ virtual QString description() const;
+
+ virtual QString id() const;
+ virtual QUrl url() const;
+
+ virtual bool hasChildren() const;
+ virtual AbstractModel *childModel() const;
+
+ virtual bool hasActions() const;
+ virtual QVariantList actions() const;
+
+ virtual bool run(const QString& actionId = QString(), const QVariant &argument = QVariant());
+
+ protected:
+ AbstractModel* m_owner;
+};
+
+class AbstractGroupEntry : public AbstractEntry
+{
+ public:
+ explicit AbstractGroupEntry(AbstractModel *owner);
+
+ EntryType type() const override { return GroupType; }
+};
+
+class SeparatorEntry : public AbstractEntry
+{
+ public:
+ explicit SeparatorEntry(AbstractModel *owner);
+
+ EntryType type() const override { return SeparatorType; }
+};
+
+#endif
diff --git a/dataengines/share/shareservice.h b/applets/kicker/plugin/abstractentry.cpp
rename from dataengines/share/shareservice.h
rename to applets/kicker/plugin/abstractentry.cpp
--- a/dataengines/share/shareservice.h
+++ b/applets/kicker/plugin/abstractentry.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2010 Artur Duque de Souza *
+ * Copyright (C) 2014-2015 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,54 +17,92 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#ifndef SHARE_SERVICE_H
-#define SHARE_SERVICE_H
+#include "abstractentry.h"
-#include "shareengine.h"
+#include
-#include
-#include
-#include
+AbstractEntry::AbstractEntry(AbstractModel *owner)
+: m_owner(owner)
+{
+}
+
+AbstractEntry::~AbstractEntry()
+{
+}
+
+AbstractModel *AbstractEntry::owner() const
+{
+ return m_owner;
+}
+
+bool AbstractEntry::isValid() const
+{
+ return true;
+}
+
+QIcon AbstractEntry::icon() const
+{
+ return QIcon();
+}
+
+QString AbstractEntry::name() const
+{
+ return QString();
+}
+
+
+QString AbstractEntry::group() const
+{
+ return QString();
+}
-class ShareProvider;
+QString AbstractEntry::description() const
+{
+ return QString();
+}
-namespace Plasma {
- class ServiceJob;
+QString AbstractEntry::id() const
+{
+ return QString();
}
-namespace KJSEmbed {
- class Engine;
+QUrl AbstractEntry::url() const
+{
+ return QUrl();
}
-class ShareService : public Plasma::Service
+bool AbstractEntry::hasChildren() const
{
- Q_OBJECT
+ return false;
+}
-public:
- explicit ShareService(ShareEngine *engine);
- Plasma::ServiceJob *createJob(const QString &operation,
- QMap ¶meters) override;
-};
+AbstractModel *AbstractEntry::childModel() const
+{
+ return nullptr;
+}
-class ShareJob : public Plasma::ServiceJob
+bool AbstractEntry::hasActions() const
{
- Q_OBJECT
+ return false;
+}
-public:
- ShareJob(const QString &destination, const QString &operation,
- QMap ¶meters, QObject *parent = nullptr);
- ~ShareJob() override;
- void start() override;
+QVariantList AbstractEntry::actions() const
+{
+ return QVariantList();
+}
-public Q_SLOTS:
- void publish();
- void showResult(const QString &url);
- void showError(const QString &msg);
+bool AbstractEntry::run(const QString& actionId, const QVariant &argument)
+{
+ Q_UNUSED(actionId)
+ Q_UNUSED(argument)
-private:
- QScopedPointer m_engine;
- ShareProvider *m_provider;
- KPackage::Package m_package;
-};
+ return false;
+}
-#endif // SHARE_SERVICE
+AbstractGroupEntry::AbstractGroupEntry(AbstractModel *owner) : AbstractEntry(owner)
+{
+}
+
+SeparatorEntry::SeparatorEntry(AbstractModel *owner) : AbstractEntry(owner)
+{
+}
diff --git a/applets/kicker/plugin/abstractmodel.h b/applets/kicker/plugin/abstractmodel.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/abstractmodel.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef ABSTRACTMODEL_H
+#define ABSTRACTMODEL_H
+
+#include
+
+class AbstractEntry;
+
+class AbstractModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
+
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(int separatorCount READ separatorCount NOTIFY separatorCountChanged)
+ Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
+ Q_PROPERTY(AbstractModel* favoritesModel READ favoritesModel WRITE setFavoritesModel NOTIFY favoritesModelChanged)
+
+ public:
+ explicit AbstractModel(QObject *parent = nullptr);
+ ~AbstractModel() override;
+
+ QHash roleNames() const override;
+
+ virtual QString description() const = 0;
+
+ int count() const;
+ virtual int separatorCount() const;
+
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ int iconSize() const;
+ void setIconSize(int size);
+
+ Q_INVOKABLE virtual bool trigger(int row, const QString &actionId, const QVariant &argument) = 0;
+
+ Q_INVOKABLE virtual void refresh();
+
+ Q_INVOKABLE virtual QString labelForRow(int row);
+
+ Q_INVOKABLE virtual AbstractModel *modelForRow(int row);
+ Q_INVOKABLE virtual int rowForModel(AbstractModel *model);
+
+ virtual bool hasActions() const;
+ virtual QVariantList actions() const;
+
+ virtual AbstractModel* favoritesModel();
+ virtual void setFavoritesModel(AbstractModel *model);
+ AbstractModel* rootModel();
+
+ virtual void entryChanged(AbstractEntry *entry);
+
+ Q_SIGNALS:
+ void descriptionChanged() const;
+ void countChanged() const;
+ void separatorCountChanged() const;
+ void iconSizeChanged() const;
+ void favoritesModelChanged() const;
+
+ protected:
+ AbstractModel *m_favoritesModel;
+
+ private:
+ int m_iconSize;
+};
+
+#endif
diff --git a/applets/kicker/plugin/abstractmodel.cpp b/applets/kicker/plugin/abstractmodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/abstractmodel.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "abstractmodel.h"
+#include "actionlist.h"
+
+AbstractModel::AbstractModel(QObject *parent) : QAbstractListModel(parent)
+, m_favoritesModel(nullptr)
+, m_iconSize(32)
+{
+}
+
+AbstractModel::~AbstractModel()
+{
+}
+
+QHash AbstractModel::roleNames() const
+{
+ QHash roles;
+ roles.insert(Qt::DisplayRole, "display");
+ roles.insert(Qt::DecorationRole, "decoration");
+ roles.insert(Kicker::GroupRole, "group");
+ roles.insert(Kicker::DescriptionRole, "description");
+ roles.insert(Kicker::FavoriteIdRole, "favoriteId");
+ roles.insert(Kicker::IsParentRole, "isParent");
+ roles.insert(Kicker::IsSeparatorRole, "isSeparator");
+ roles.insert(Kicker::HasChildrenRole, "hasChildren");
+ roles.insert(Kicker::HasActionListRole, "hasActionList");
+ roles.insert(Kicker::ActionListRole, "actionList");
+ roles.insert(Kicker::UrlRole, "url");
+
+ return roles;
+}
+
+int AbstractModel::count() const
+{
+ return rowCount();
+}
+
+int AbstractModel::separatorCount() const
+{
+ return 0;
+}
+
+int AbstractModel::columnCount(const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int AbstractModel::iconSize() const
+{
+ return m_iconSize;
+}
+
+void AbstractModel::setIconSize(int iconSize) {
+ if (m_iconSize != iconSize) {
+ m_iconSize = iconSize;
+ refresh();
+ }
+}
+
+void AbstractModel::refresh()
+{
+}
+
+QString AbstractModel::labelForRow(int row)
+{
+ return data(index(row, 0), Qt::DisplayRole).toString();
+}
+
+AbstractModel *AbstractModel::modelForRow(int row)
+{
+ Q_UNUSED(row)
+
+ return nullptr;
+}
+
+int AbstractModel::rowForModel(AbstractModel *model)
+{
+ Q_UNUSED(model)
+
+ return -1;
+}
+
+bool AbstractModel::hasActions() const
+{
+ return false;
+}
+
+QVariantList AbstractModel::actions() const
+{
+ return QVariantList();
+}
+
+AbstractModel* AbstractModel::favoritesModel()
+{
+ if (m_favoritesModel) {
+ return m_favoritesModel;
+ } else {
+ AbstractModel *model = rootModel();
+
+ if (model && model != this) {
+ return model->favoritesModel();
+ }
+ }
+
+ return nullptr;
+}
+
+void AbstractModel::setFavoritesModel(AbstractModel *model)
+{
+ if (m_favoritesModel != model) {
+ m_favoritesModel = model;
+
+ emit favoritesModelChanged();
+ }
+}
+
+AbstractModel* AbstractModel::rootModel()
+{
+ if (!parent()) {
+ return nullptr;
+ }
+
+ QObject *p = this;
+ AbstractModel *rootModel = nullptr;
+
+ while (p) {
+ if (qobject_cast(p)) {
+ rootModel = qobject_cast(p);
+ } else {
+ return rootModel;
+ }
+
+ p = p->parent();
+ }
+
+ return rootModel;
+}
+
+void AbstractModel::entryChanged(AbstractEntry *entry)
+{
+ Q_UNUSED(entry)
+}
diff --git a/applets/kicker/plugin/actionlist.h b/applets/kicker/plugin/actionlist.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/actionlist.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Aurélien Gâteau *
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef ACTIONLIST_H
+#define ACTIONLIST_H
+
+#include
+
+#include
+
+class KFileItem;
+
+namespace Kicker
+{
+
+enum {
+ DescriptionRole = Qt::UserRole + 1,
+ GroupRole,
+ FavoriteIdRole,
+ IsSeparatorRole,
+ IsDropPlaceholderRole,
+ IsParentRole,
+ HasChildrenRole,
+ HasActionListRole,
+ ActionListRole,
+ UrlRole
+};
+
+QVariantMap createActionItem(const QString &label, const QString &icon, const QString &actionId, const QVariant &argument = QVariant());
+
+QVariantMap createTitleActionItem(const QString &label);
+
+QVariantMap createSeparatorActionItem();
+
+QVariantList createActionListForFileItem(const KFileItem &fileItem);
+bool handleFileItemAction(const KFileItem &fileItem, const QString &actionId, const QVariant &argument, bool *close);
+
+QVariantList createAddLauncherActionList(QObject *appletInterface, const KService::Ptr &service);
+bool handleAddLauncherAction(const QString &actionId, QObject *appletInterface, const KService::Ptr &service);
+
+QVariantList jumpListActions(KService::Ptr service);
+
+QVariantList recentDocumentActions(KService::Ptr service);
+bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &argument);
+
+bool canEditApplication(const QString &entryPath);
+void editApplication(const QString &entryPath, const QString &menuId);
+QVariantList editApplicationAction(const KService::Ptr &service);
+bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &service);
+
+QVariantList appstreamActions(const KService::Ptr &service);
+bool handleAppstreamActions(const QString &actionId, const QVariant &argument);
+
+QString resolvedServiceEntryPath(const KService::Ptr &service);
+
+}
+
+#endif
diff --git a/applets/kicker/plugin/actionlist.cpp b/applets/kicker/plugin/actionlist.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/actionlist.cpp
@@ -0,0 +1,415 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Aurélien Gâteau *
+ * Copyright (C) 2014 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "actionlist.h"
+#include "menuentryeditor.h"
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "containmentinterface.h"
+
+#ifdef HAVE_APPSTREAMQT
+#include
+#endif
+
+namespace KAStats = KActivities::Stats;
+
+using namespace KAStats;
+using namespace KAStats::Terms;
+
+namespace Kicker
+{
+
+QVariantMap createActionItem(const QString &label, const QString &icon, const QString &actionId, const QVariant &argument)
+{
+ QVariantMap map;
+
+ map[QStringLiteral("text")] = label;
+ map[QStringLiteral("icon")] = icon;
+ map[QStringLiteral("actionId")] = actionId;
+
+ if (argument.isValid()) {
+ map[QStringLiteral("actionArgument")] = argument;
+ }
+
+ return map;
+}
+
+QVariantMap createTitleActionItem(const QString &label)
+{
+ QVariantMap map;
+
+ map[QStringLiteral("text")] = label;
+ map[QStringLiteral("type")] = QStringLiteral("title");
+
+ return map;
+}
+
+QVariantMap createSeparatorActionItem()
+{
+ QVariantMap map;
+
+ map[QStringLiteral("type")] = QStringLiteral("separator");
+
+ return map;
+}
+
+QVariantList createActionListForFileItem(const KFileItem &fileItem)
+{
+ QVariantList list;
+
+ KService::List services = KMimeTypeTrader::self()->query(fileItem.mimetype(), QStringLiteral("Application"));
+
+ if (!services.isEmpty()) {
+ list << createTitleActionItem(i18n("Open with:"));
+
+ foreach (const KService::Ptr service, services) {
+ const QString text = service->name().replace(QLatin1Char('&'), QStringLiteral("&&"));
+ QVariantMap item = createActionItem(text, service->icon(), QStringLiteral("_kicker_fileItem_openWith"), service->entryPath());
+
+ list << item;
+ }
+
+ list << createSeparatorActionItem();
+ }
+
+ const QVariantMap &propertiesItem = createActionItem(i18n("Properties"), QStringLiteral("document-properties"), QStringLiteral("_kicker_fileItem_properties"));
+ list << propertiesItem;
+
+ return list;
+}
+
+bool handleFileItemAction(const KFileItem &fileItem, const QString &actionId, const QVariant &argument, bool *close)
+{
+ if (actionId == QLatin1String("_kicker_fileItem_properties")) {
+ KPropertiesDialog *dlg = new KPropertiesDialog(fileItem, QApplication::activeWindow());
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->show();
+
+ *close = false;
+
+ return true;
+ }
+
+ if (actionId == QLatin1String("_kicker_fileItem_openWith")) {
+ const QString path = argument.toString();
+ const KService::Ptr service = KService::serviceByDesktopPath(path);
+
+ if (!service) {
+ return false;
+ }
+
+ KRun::runService(*service, QList() << fileItem.url(), QApplication::activeWindow());
+
+ *close = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+QVariantList createAddLauncherActionList(QObject *appletInterface, const KService::Ptr &service)
+{
+ QVariantList actionList;
+ if (!service) {
+ return actionList;
+ }
+
+ if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) {
+ QVariantMap addToDesktopAction = Kicker::createActionItem(i18n("Add to Desktop"), QStringLiteral("list-add"), QStringLiteral("addToDesktop"));
+ actionList << addToDesktopAction;
+ }
+
+ if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) {
+ QVariantMap addToPanelAction = Kicker::createActionItem(i18n("Add to Panel (Widget)"), QStringLiteral("list-add"), QStringLiteral("addToPanel"));
+ actionList << addToPanelAction;
+ }
+
+ if (service && ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service))) {
+ QVariantMap addToTaskManagerAction = Kicker::createActionItem(i18n("Pin to Task Manager"), QStringLiteral("pin"), QStringLiteral("addToTaskManager"));
+ actionList << addToTaskManagerAction;
+ }
+
+ return actionList;
+}
+
+bool handleAddLauncherAction(const QString &actionId, QObject *appletInterface, const KService::Ptr &service)
+{
+ if (!service) {
+ return false;
+ }
+
+ if (actionId == QLatin1String("addToDesktop")) {
+ if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) {
+ ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Desktop, Kicker::resolvedServiceEntryPath(service));
+ }
+ return true;
+ } else if (actionId == QLatin1String("addToPanel")) {
+ if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) {
+ ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Panel, Kicker::resolvedServiceEntryPath(service));
+ }
+ return true;
+ } else if (actionId == QLatin1String("addToTaskManager")) {
+ if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service))) {
+ ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::TaskManager, Kicker::resolvedServiceEntryPath(service));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+QString storageIdFromService(KService::Ptr service)
+{
+ QString storageId = service->storageId();
+
+ if (storageId.endsWith(QLatin1String(".desktop"))) {
+ storageId = storageId.left(storageId.length() - 8);
+ }
+
+ return storageId;
+}
+
+QVariantList jumpListActions(KService::Ptr service)
+{
+ QVariantList list;
+
+ if (!service) {
+ return list;
+ }
+
+ const auto &actions = service->actions();
+ foreach (const KServiceAction &action, actions) {
+ if (action.text().isEmpty() || action.exec().isEmpty()) {
+ continue;
+ }
+
+ QVariantMap item = createActionItem(action.text(), action.icon(), QStringLiteral("_kicker_jumpListAction"), action.exec());
+
+ list << item;
+ }
+
+ return list;
+}
+
+QVariantList recentDocumentActions(KService::Ptr service)
+{
+ QVariantList list;
+
+ if (!service) {
+ return list;
+ }
+
+ const QString storageId = storageIdFromService(service);
+
+ if (storageId.isEmpty()) {
+ return list;
+ }
+
+ auto query = UsedResources
+ | RecentlyUsedFirst
+ | Agent(storageId)
+ | Type::any()
+ | Activity::current()
+ | Url::file();
+
+ ResultSet results(query);
+
+ ResultSet::const_iterator resultIt;
+ resultIt = results.begin();
+
+ while (list.count() < 6 && resultIt != results.end()) {
+ const QString resource = (*resultIt).resource();
+ ++resultIt;
+
+ const QUrl url(resource);
+
+ if (!url.isValid()) {
+ continue;
+ }
+
+ const KFileItem fileItem(url);
+
+ if (!fileItem.isFile()) {
+ continue;
+ }
+
+ if (list.isEmpty()) {
+ list << createTitleActionItem(i18n("Recent Documents"));
+ }
+
+ QVariantMap item = createActionItem(url.fileName(), fileItem.iconName(), QStringLiteral("_kicker_recentDocument"), resource);
+
+ list << item;
+ }
+
+ if (!list.isEmpty()) {
+ QVariantMap forgetAction = createActionItem(i18n("Forget Recent Documents"), QStringLiteral("edit-clear-history"), QStringLiteral("_kicker_forgetRecentDocuments"));
+ list << forgetAction;
+ }
+
+ return list;
+}
+
+bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &_argument)
+{
+ if (!service) {
+ return false;
+ }
+
+ if (actionId == QLatin1String("_kicker_forgetRecentDocuments")) {
+ const QString storageId = storageIdFromService(service);
+
+ if (storageId.isEmpty()) {
+ return false;
+ }
+
+ auto query = UsedResources
+ | Agent(storageId)
+ | Type::any()
+ | Activity::current()
+ | Url::file();
+
+ KAStats::forgetResources(query);
+
+ return false;
+ }
+
+ QString argument = _argument.toString();
+
+ if (argument.isEmpty()) {
+ return false;
+ }
+
+ return (KRun::runService(*service, QList() << QUrl(argument), QApplication::activeWindow()) != 0);
+}
+
+Q_GLOBAL_STATIC(MenuEntryEditor, menuEntryEditor)
+
+bool canEditApplication(const KService::Ptr &service)
+{
+ return (service->isApplication() && menuEntryEditor->canEdit(service->entryPath()));
+}
+
+void editApplication(const QString &entryPath, const QString &menuId)
+{
+ menuEntryEditor->edit(entryPath, menuId);
+}
+
+QVariantList editApplicationAction(const KService::Ptr &service)
+{
+ QVariantList actionList;
+
+ if (canEditApplication(service)) {
+ // TODO: Using the KMenuEdit icon might be misleading.
+ QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), QStringLiteral("kmenuedit"), QStringLiteral("editApplication"));
+ actionList << editAction;
+ }
+
+ return actionList;
+}
+
+bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &service)
+{
+
+ if (service && actionId ==QLatin1String("editApplication") && canEditApplication(service)) {
+ Kicker::editApplication(service->entryPath(), service->menuId());
+
+ return true;
+ }
+
+ return false;
+}
+
+#ifdef HAVE_APPSTREAMQT
+Q_GLOBAL_STATIC(AppStream::Pool, appstreamPool)
+#endif
+
+QVariantList appstreamActions(const KService::Ptr &service)
+{
+ QVariantList ret;
+
+#ifdef HAVE_APPSTREAMQT
+ const KService::Ptr appStreamHandler = KMimeTypeTrader::self()->preferredService(QStringLiteral("x-scheme-handler/appstream"));
+
+ // Don't show action if we can't find any app to handle appstream:// URLs.
+ if (!appStreamHandler) {
+ if (!KProtocolInfo::isHelperProtocol(QStringLiteral("appstream"))
+ || KProtocolInfo::exec(QStringLiteral("appstream")).isEmpty()) {
+ return ret;
+ }
+ }
+
+ if (!appstreamPool.exists()) {
+ appstreamPool->load();
+ }
+
+ const auto components = appstreamPool->componentsById(service->desktopEntryName()+QLatin1String(".desktop"));
+ for(const auto &component: components) {
+ const QString componentId = component.id();
+
+ QVariantMap appstreamAction = Kicker::createActionItem(
+ i18nc("@action opens a software center with the application", "Uninstall or Manage Add-Ons..."),
+ appStreamHandler->icon(),
+ "manageApplication", QVariant(QLatin1String("appstream://") + componentId));
+ ret << appstreamAction;
+ }
+#else
+ Q_UNUSED(service)
+#endif
+
+ return ret;
+}
+
+bool handleAppstreamActions(const QString &actionId, const QVariant &argument)
+{
+ if (actionId == QLatin1String("manageApplication")) {
+ return QDesktopServices::openUrl(QUrl(argument.toString()));
+ }
+
+ return false;
+}
+
+QString resolvedServiceEntryPath(const KService::Ptr &service)
+{
+ QString path = service->entryPath();
+ if (!QDir::isAbsolutePath(path)) {
+ path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kservices5/") + path);
+ }
+ return path;
+}
+
+}
diff --git a/applets/kicker/plugin/appentry.h b/applets/kicker/plugin/appentry.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/appentry.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * Copyright (C) 201 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef APPENTRY_H
+#define APPENTRY_H
+
+#include "abstractentry.h"
+
+#include
+#include
+
+class AppsModel;
+class MenuEntryEditor;
+
+class AppEntry : public AbstractEntry
+{
+ public:
+ enum NameFormat {
+ NameOnly = 0,
+ GenericNameOnly,
+ NameAndGenericName,
+ GenericNameAndName
+ };
+
+ explicit AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat);
+ explicit AppEntry(AbstractModel *owner, const QString &id);
+
+ EntryType type() const override { return RunnableType; }
+
+ bool isValid() const override;
+
+ QIcon icon() const override;
+ QString name() const override;
+ QString description() const override;
+ KService::Ptr service() const;
+
+ QString id() const override;
+ QUrl url() const override;
+
+ bool hasActions() const override;
+ QVariantList actions() const override;
+
+ bool run(const QString& actionId = QString(), const QVariant &argument = QVariant()) override;
+
+ QString menuId() const;
+
+ static QString nameFromService(const KService::Ptr service, NameFormat nameFormat);
+ static KService::Ptr defaultAppByName(const QString &name);
+
+ private:
+ void init(NameFormat nameFormat);
+
+ QString m_id;
+ QString m_name;
+ QString m_description;
+ mutable QIcon m_icon;
+ KService::Ptr m_service;
+ static MenuEntryEditor *m_menuEntryEditor;
+};
+
+class AppGroupEntry : public AbstractGroupEntry
+{
+ public:
+ AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group,
+ bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat);
+
+ QIcon icon() const override;
+ QString name() const override;
+
+ bool hasChildren() const override;
+ AbstractModel *childModel() const override;
+
+ private:
+ KServiceGroup::Ptr m_group;
+ mutable QIcon m_icon;
+ QPointer m_childModel;
+};
+
+#endif
diff --git a/applets/kicker/plugin/appentry.cpp b/applets/kicker/plugin/appentry.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/appentry.cpp
@@ -0,0 +1,307 @@
+/***************************************************************************
+ * Copyright (C) 201 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include
+#include "appentry.h"
+#include "actionlist.h"
+#include "appsmodel.h"
+#include "containmentinterface.h"
+
+#include
+
+#include
+#include
+#include
+#if HAVE_X11
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat)
+: AbstractEntry(owner)
+, m_service(service)
+{
+ if (m_service) {
+ init(nameFormat);
+ }
+}
+
+AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner)
+{
+ const QUrl url(id);
+
+ if (url.scheme() == QLatin1String("preferred")) {
+ m_service = defaultAppByName(url.host());
+ m_id = id;
+ } else {
+ m_service = KService::serviceByStorageId(id);
+ }
+
+ if (m_service) {
+ init((NameFormat)owner->rootModel()->property("appNameFormat").toInt());
+ }
+}
+
+void AppEntry::init(NameFormat nameFormat)
+{
+ m_name = nameFromService(m_service, nameFormat);
+
+ if (nameFormat == GenericNameOnly) {
+ m_description = nameFromService(m_service, NameOnly);
+ } else {
+ m_description = nameFromService(m_service, GenericNameOnly);
+ }
+}
+
+bool AppEntry::isValid() const
+{
+ return m_service;
+}
+
+QIcon AppEntry::icon() const
+{
+ if (m_icon.isNull()) {
+ m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
+ }
+ return m_icon;
+}
+
+QString AppEntry::name() const
+{
+ return m_name;
+}
+
+QString AppEntry::description() const
+{
+ return m_description;
+}
+
+KService::Ptr AppEntry::service() const
+{
+ return m_service;
+}
+
+QString AppEntry::id() const
+{
+ if (!m_id.isEmpty()) {
+ return m_id;
+ }
+
+ return m_service->storageId();
+}
+
+QString AppEntry::menuId() const
+{
+ return m_service->menuId();
+}
+
+QUrl AppEntry::url() const
+{
+ return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(m_service));
+}
+
+bool AppEntry::hasActions() const
+{
+ return true;
+}
+
+QVariantList AppEntry::actions() const
+{
+ QVariantList actionList;
+
+ actionList << Kicker::jumpListActions(m_service);
+ if (!actionList.isEmpty()) {
+ actionList << Kicker::createSeparatorActionItem();
+ }
+
+ QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value();
+
+ bool systemImmutable = false;
+ if (appletInterface) {
+ systemImmutable = (appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable);
+ }
+
+ const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service);
+ if (!systemImmutable && !addLauncherActions.isEmpty()) {
+ actionList << addLauncherActions
+ << Kicker::createSeparatorActionItem();
+ }
+
+ const QVariantList &recentDocuments = Kicker::recentDocumentActions(m_service);
+ if (!recentDocuments.isEmpty()) {
+ actionList << recentDocuments << Kicker::createSeparatorActionItem();
+ }
+
+ // Don't allow adding launchers, editing, hiding, or uninstalling applications
+ // when system is immutable.
+ if (systemImmutable) {
+ return actionList;
+ }
+
+ if (m_service->isApplication()) {
+ actionList << Kicker::createSeparatorActionItem();
+ actionList << Kicker::editApplicationAction(m_service);
+ actionList << Kicker::appstreamActions(m_service);
+ }
+
+ if (appletInterface) {
+ QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value());
+
+ if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications")) && qobject_cast(m_owner)) {
+ const QStringList &hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
+
+ if (!hiddenApps.contains(m_service->menuId())) {
+ QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("view-hidden"), QStringLiteral("hideApplication"));
+ actionList << hideAction;
+ }
+ }
+ }
+
+ return actionList;
+}
+
+bool AppEntry::run(const QString& actionId, const QVariant &argument)
+{
+ if (!m_service->isValid()) {
+ return false;
+ }
+
+ if (actionId.isEmpty()) {
+ quint32 timeStamp = 0;
+
+#if HAVE_X11
+ if (QX11Info::isPlatformX11()) {
+ timeStamp = QX11Info::appUserTime();
+ }
+#endif
+
+ KRun::runApplication(*m_service, {}, nullptr, KRun::DeleteTemporaryFiles, {}, KStartupInfo::createNewStartupIdForTimestamp(timeStamp));
+
+ KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + m_service->storageId()),
+ QStringLiteral("org.kde.plasma.kicker"));
+
+ return true;
+ }
+
+ QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value();
+
+ if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) {
+ return true;
+ } else if (Kicker::handleEditApplicationAction(actionId, m_service)) {
+ return true;
+ } else if (Kicker::handleAppstreamActions(actionId, argument)) {
+ return true;
+ } else if (actionId == QLatin1String("_kicker_jumpListAction")) {
+ return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon());
+ }
+
+ return Kicker::handleRecentDocumentAction(m_service, actionId, argument);
+}
+
+QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat)
+{
+ const QString &name = service->name();
+ QString genericName = service->genericName();
+
+ if (genericName.isEmpty()) {
+ genericName = service->comment();
+ }
+
+ if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) {
+ return name;
+ } else if (nameFormat == GenericNameOnly) {
+ return genericName;
+ } else if (nameFormat == NameAndGenericName) {
+ return i18nc("App name (Generic name)", "%1 (%2)", name, genericName);
+ } else {
+ return i18nc("Generic name (App name)", "%1 (%2)", genericName, name);
+ }
+}
+
+KService::Ptr AppEntry::defaultAppByName(const QString& name)
+{
+ if (name == QLatin1String("browser")) {
+ KConfigGroup config(KSharedConfig::openConfig(), "General");
+ QString browser = config.readPathEntry("BrowserApplication", QString());
+
+ if (browser.isEmpty()) {
+ return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html"));
+ } else if (browser.startsWith(QLatin1Char('!'))) {
+ browser.remove(0, 1);
+ }
+
+ return KService::serviceByStorageId(browser);
+ }
+
+ return KService::Ptr();
+}
+
+AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group,
+ bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel),
+ m_group(group)
+{
+ AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat,
+ sorted, separators, parentModel);
+ model->setAppNameFormat(appNameFormat);
+ m_childModel = model;
+
+ QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater);
+
+ QObject::connect(model, &AppsModel::countChanged,
+ [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }
+ );
+
+ QObject::connect(model, &AppsModel::hiddenEntriesChanged,
+ [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }
+ );
+}
+
+QIcon AppGroupEntry::icon() const
+{
+ if (m_icon.isNull()) {
+ m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
+ }
+ return m_icon;
+}
+
+QString AppGroupEntry::name() const
+{
+ return m_group->caption();
+}
+
+bool AppGroupEntry::hasChildren() const
+{
+ return m_childModel && m_childModel->count() > 0;
+}
+
+AbstractModel *AppGroupEntry::childModel() const {
+ return m_childModel;
+}
diff --git a/applets/kicker/plugin/appsmodel.h b/applets/kicker/plugin/appsmodel.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/appsmodel.h
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * Copyright (C) 2012 Aurélien Gâteau *
+ * Copyright (C) 2013-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef APPSMODEL_H
+#define APPSMODEL_H
+
+#include "abstractmodel.h"
+#include "appentry.h"
+
+#include
+
+#include
+
+
+class QTimer;
+
+class AppsModel : public AbstractModel, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+
+ Q_PROPERTY(bool autoPopulate READ autoPopulate WRITE setAutoPopulate NOTIFY autoPopulateChanged)
+
+ Q_PROPERTY(bool paginate READ paginate WRITE setPaginate NOTIFY paginateChanged)
+ Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize NOTIFY pageSizeChanged)
+ Q_PROPERTY(bool flat READ flat WRITE setFlat NOTIFY flatChanged)
+ Q_PROPERTY(bool sorted READ sorted WRITE setSorted NOTIFY sortedChanged)
+ Q_PROPERTY(bool showSeparators READ showSeparators WRITE setShowSeparators NOTIFY showSeparatorsChanged)
+ Q_PROPERTY(bool showTopLevelItems READ showTopLevelItems WRITE setShowTopLevelItems NOTIFY showTopLevelItemsChanged)
+ Q_PROPERTY(int appNameFormat READ appNameFormat WRITE setAppNameFormat NOTIFY appNameFormatChanged)
+ Q_PROPERTY(QObject* appletInterface READ appletInterface WRITE setAppletInterface NOTIFY appletInterfaceChanged)
+
+ public:
+ explicit AppsModel(const QString &entryPath = QString(), bool paginate = false, int pageSize = 24,
+ bool flat = false, bool sorted = true, bool separators = true, QObject *parent = nullptr);
+ explicit AppsModel(const QList entryList, bool deleteEntriesOnDestruction, QObject *parent = nullptr);
+ ~AppsModel() override;
+
+ QString description() const override;
+ void setDescription(const QString &text);
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) override;
+
+ bool autoPopulate() const;
+ void setAutoPopulate(bool populate);
+
+ Q_INVOKABLE AbstractModel *modelForRow(int row) override;
+ Q_INVOKABLE int rowForModel(AbstractModel *model) override;
+
+ int separatorCount() const override;
+
+ bool paginate() const;
+ void setPaginate(bool paginate);
+
+ int pageSize() const;
+ void setPageSize(int size);
+
+ bool flat() const;
+ void setFlat(bool flat);
+
+ bool sorted() const;
+ void setSorted(bool sorted);
+
+ bool showSeparators() const;
+ void setShowSeparators(bool showSeparators);
+
+ bool showTopLevelItems() const;
+ void setShowTopLevelItems(bool showTopLevelItems);
+
+ int appNameFormat() const;
+ void setAppNameFormat(int format);
+
+ QObject *appletInterface() const;
+ void setAppletInterface(QObject *appletInterface);
+
+ QStringList hiddenEntries() const;
+
+ void entryChanged(AbstractEntry *entry) override;
+
+ void classBegin() override;
+ void componentComplete() override;
+
+ Q_SIGNALS:
+ void cleared() const;
+ void autoPopulateChanged() const;
+ void paginateChanged() const;
+ void pageSizeChanged() const;
+ void flatChanged() const;
+ void sortedChanged() const;
+ void showSeparatorsChanged() const;
+ void showTopLevelItemsChanged() const;
+ void appNameFormatChanged() const;
+ void appletInterfaceChanged() const;
+ void hiddenEntriesChanged() const;
+
+ protected Q_SLOTS:
+ void refresh() override;
+
+ protected:
+ void refreshInternal();
+
+ bool m_complete;
+
+ bool m_paginate;
+ int m_pageSize;
+
+ QList m_entryList;
+ bool m_deleteEntriesOnDestruction;
+ int m_separatorCount;
+ bool m_showSeparators;
+ bool m_showTopLevelItems;
+
+ QObject *m_appletInterface;
+
+ private Q_SLOTS:
+ void checkSycocaChanges(const QStringList &changes);
+
+ private:
+ void processServiceGroup(KServiceGroup::Ptr group);
+ void sortEntries();
+
+ bool m_autoPopulate;
+
+ QString m_description;
+ QString m_entryPath;
+ bool m_staticEntryList;
+ QTimer *m_changeTimer;
+ bool m_flat;
+ bool m_sorted;
+ AppEntry::NameFormat m_appNameFormat;
+ QStringList m_hiddenEntries;
+ static MenuEntryEditor *m_menuEntryEditor;
+};
+
+#endif
diff --git a/applets/kicker/plugin/appsmodel.cpp b/applets/kicker/plugin/appsmodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/appsmodel.cpp
@@ -0,0 +1,746 @@
+/***************************************************************************
+ * Copyright (C) 2012 Aurélien Gâteau *
+ * Copyright (C) 2013-2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "appsmodel.h"
+#include "actionlist.h"
+#include "rootmodel.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+AppsModel::AppsModel(const QString &entryPath, bool paginate, int pageSize, bool flat,
+ bool sorted, bool separators, QObject *parent)
+: AbstractModel(parent)
+, m_complete(false)
+, m_paginate(paginate)
+, m_pageSize(pageSize)
+, m_deleteEntriesOnDestruction(true)
+, m_separatorCount(0)
+, m_showSeparators(separators)
+, m_showTopLevelItems(false)
+, m_appletInterface(nullptr)
+, m_autoPopulate(true)
+, m_description(i18n("Applications"))
+, m_entryPath(entryPath)
+, m_staticEntryList(false)
+, m_changeTimer(nullptr)
+, m_flat(flat)
+, m_sorted(sorted)
+, m_appNameFormat(AppEntry::NameOnly)
+{
+ if (!m_entryPath.isEmpty()) {
+ componentComplete();
+ }
+}
+
+AppsModel::AppsModel(const QList entryList, bool deleteEntriesOnDestruction, QObject *parent)
+: AbstractModel(parent)
+, m_complete(false)
+, m_paginate(false)
+, m_pageSize(24)
+, m_deleteEntriesOnDestruction(deleteEntriesOnDestruction)
+, m_separatorCount(0)
+, m_showSeparators(false)
+, m_showTopLevelItems(false)
+, m_appletInterface(nullptr)
+, m_autoPopulate(true)
+, m_description(i18n("Applications"))
+, m_entryPath(QString())
+, m_staticEntryList(true)
+, m_changeTimer(nullptr)
+, m_flat(true)
+, m_sorted(true)
+, m_appNameFormat(AppEntry::NameOnly)
+{
+ foreach(AbstractEntry *suggestedEntry, entryList) {
+ bool found = false;
+
+ foreach (const AbstractEntry *entry, m_entryList) {
+ if (entry->type() == AbstractEntry::RunnableType
+ && static_cast(entry)->service()->storageId()
+ == static_cast(suggestedEntry)->service()->storageId()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ m_entryList << suggestedEntry;
+ }
+ }
+
+ sortEntries();
+}
+
+AppsModel::~AppsModel()
+{
+ if (m_deleteEntriesOnDestruction) {
+ qDeleteAll(m_entryList);
+ }
+}
+
+bool AppsModel::autoPopulate() const
+{
+ return m_autoPopulate;
+}
+
+void AppsModel::setAutoPopulate(bool populate)
+{
+ if (m_autoPopulate != populate) {
+ m_autoPopulate = populate;
+
+ emit autoPopulateChanged();
+ }
+}
+
+QString AppsModel::description() const
+{
+ return m_description;
+}
+
+void AppsModel::setDescription(const QString &text)
+{
+ if (m_description != text) {
+ m_description = text;
+
+ emit descriptionChanged();
+ }
+}
+
+QVariant AppsModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= m_entryList.count()) {
+ return QVariant();
+ }
+
+ const AbstractEntry *entry = m_entryList.at(index.row());
+
+ if (role == Qt::DisplayRole) {
+ return entry->name();
+ } else if (role == Qt::DecorationRole) {
+ return entry->icon();
+ } else if (role == Kicker::DescriptionRole) {
+ return entry->description();
+ } else if (role == Kicker::FavoriteIdRole && entry->type() == AbstractEntry::RunnableType) {
+ return entry->id();
+ } else if (role == Kicker::UrlRole && entry->type() == AbstractEntry::RunnableType) {
+ return entry->url();
+ } else if (role == Kicker::IsParentRole) {
+ return (entry->type() == AbstractEntry::GroupType);
+ } else if (role == Kicker::IsSeparatorRole) {
+ return (entry->type() == AbstractEntry::SeparatorType);
+ } else if (role == Kicker::HasChildrenRole) {
+ return entry->hasChildren();
+ } else if (role == Kicker::HasActionListRole) {
+ const AppsModel *appsModel = qobject_cast(entry->childModel());
+
+ return entry->hasActions() || (appsModel && !appsModel->hiddenEntries().isEmpty());
+ } else if (role == Kicker::ActionListRole) {
+ QVariantList actionList = entry->actions();
+
+ if (!m_hiddenEntries.isEmpty()) {
+ actionList << Kicker::createSeparatorActionItem();
+ QVariantMap unhideSiblingApplicationsAction = Kicker::createActionItem(i18n("Unhide Applications in this Submenu"), QStringLiteral("view-visible"), QStringLiteral("unhideSiblingApplications"));
+ actionList << unhideSiblingApplicationsAction;
+ }
+
+ const AppsModel *appsModel = qobject_cast(entry->childModel());
+
+ if (appsModel && !appsModel->hiddenEntries().isEmpty()) {
+ QVariantMap unhideChildApplicationsAction = Kicker::createActionItem(i18n("Unhide Applications in '%1'", entry->name()), QStringLiteral("view-visible"), QStringLiteral("unhideChildApplications"));
+ actionList << unhideChildApplicationsAction;
+ }
+
+ return actionList;
+ }
+
+ return QVariant();
+}
+
+QModelIndex AppsModel::index(int row, int column, const QModelIndex &parent) const
+{
+ return hasIndex(row, column, parent) ? createIndex(row, column, m_entryList.at(row)) : QModelIndex();
+}
+
+
+int AppsModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : m_entryList.count();
+}
+
+bool AppsModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ if (row < 0 || row >= m_entryList.count()) {
+ return false;
+ }
+
+ AbstractEntry *entry = m_entryList.at(row);
+
+ if (actionId == QLatin1String("hideApplication") && entry->type() == AbstractEntry::RunnableType) {
+ QObject *appletInterface = rootModel()->property("appletInterface").value();
+ QQmlPropertyMap *appletConfig = nullptr;
+ if (appletInterface) {
+ appletConfig = qobject_cast(appletInterface->property("configuration").value());
+ }
+
+ if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications"))) {
+ QStringList hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
+
+ KService::Ptr service = static_cast(entry)->service();
+
+ if (!hiddenApps.contains(service->menuId())) {
+ hiddenApps << service->menuId();
+
+ appletConfig->insert(QLatin1String("hiddenApplications"), hiddenApps);
+ QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection,
+ Q_ARG(QString, QStringLiteral("hiddenApplications")),
+ Q_ARG(QVariant, hiddenApps));
+
+ refresh();
+
+ emit hiddenEntriesChanged();
+ }
+ }
+
+ return false;
+ } else if (actionId == QLatin1String("unhideSiblingApplications")) {
+ QObject *appletInterface = rootModel()->property("appletInterface").value();
+ QQmlPropertyMap *appletConfig = nullptr;
+ if (appletInterface) {
+ appletConfig = qobject_cast(appletInterface->property("configuration").value());
+ }
+
+ if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications"))) {
+ QStringList hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
+
+ foreach(const QString& app, m_hiddenEntries) {
+ hiddenApps.removeOne(app);
+ }
+
+ appletConfig->insert(QStringLiteral("hiddenApplications"), hiddenApps);
+ QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection,
+ Q_ARG(QString, QStringLiteral("hiddenApplications")),
+ Q_ARG(QVariant, hiddenApps));
+
+ m_hiddenEntries.clear();
+
+ refresh();
+
+ emit hiddenEntriesChanged();
+ }
+
+ return false;
+ } else if (actionId == QLatin1String("unhideChildApplications")) {
+ QObject *appletInterface = rootModel()->property("appletInterface").value();
+ QQmlPropertyMap *appletConfig = nullptr;
+ if (appletInterface) {
+ appletConfig = qobject_cast(appletInterface->property("configuration").value());
+ }
+
+ if (entry->type() == AbstractEntry::GroupType
+ && appletConfig && appletConfig->contains(QLatin1String("hiddenApplications"))) {
+
+ const AppsModel *appsModel = qobject_cast(entry->childModel());
+
+ if (!appsModel) {
+ return false;
+ }
+
+ QStringList hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
+
+ foreach(const QString& app, appsModel->hiddenEntries()) {
+ hiddenApps.removeOne(app);
+ }
+
+ appletConfig->insert(QStringLiteral("hiddenApplications"), hiddenApps);
+ QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection,
+ Q_ARG(QString, QStringLiteral("hiddenApplications")),
+ Q_ARG(QVariant, hiddenApps));
+
+ refresh();
+
+ emit hiddenEntriesChanged();
+ }
+
+ return false;
+ }
+
+ return entry->run(actionId, argument);
+}
+
+AbstractModel *AppsModel::modelForRow(int row)
+{
+ if (row < 0 || row >= m_entryList.count()) {
+ return nullptr;
+ }
+
+ return m_entryList.at(row)->childModel();
+}
+
+int AppsModel::rowForModel(AbstractModel *model)
+{
+ for (int i = 0; i < m_entryList.count(); ++i) {
+ if (m_entryList.at(i)->childModel() == model) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int AppsModel::separatorCount() const
+{
+ return m_separatorCount;
+}
+
+bool AppsModel::paginate() const
+{
+ return m_paginate;
+}
+
+void AppsModel::setPaginate(bool paginate)
+{
+ if (m_paginate != paginate) {
+ m_paginate = paginate;
+
+ refresh();
+
+ emit paginateChanged();
+ }
+}
+
+int AppsModel::pageSize() const
+{
+ return m_pageSize;
+}
+
+void AppsModel::setPageSize(int size)
+{
+ if (m_pageSize != size) {
+ m_pageSize = size;
+
+ refresh();
+
+ emit pageSizeChanged();
+ }
+}
+
+bool AppsModel::flat() const
+{
+ return m_flat;
+}
+
+void AppsModel::setFlat(bool flat)
+{
+ if (m_flat != flat) {
+ m_flat = flat;
+
+ refresh();
+
+ emit flatChanged();
+ }
+}
+
+bool AppsModel::sorted() const
+{
+ return m_sorted;
+}
+
+void AppsModel::setSorted(bool sorted)
+{
+ if (m_sorted != sorted) {
+ m_sorted = sorted;
+
+ refresh();
+
+ emit sortedChanged();
+ }
+}
+
+bool AppsModel::showSeparators() const
+{
+ return m_showSeparators;
+}
+
+void AppsModel::setShowSeparators(bool showSeparators) {
+ if (m_showSeparators != showSeparators) {
+ m_showSeparators = showSeparators;
+
+ refresh();
+
+ emit showSeparatorsChanged();
+ }
+}
+
+bool AppsModel::showTopLevelItems() const
+{
+ return m_showTopLevelItems;
+}
+
+void AppsModel::setShowTopLevelItems(bool showTopLevelItems) {
+ if (m_showTopLevelItems != showTopLevelItems) {
+ m_showTopLevelItems = showTopLevelItems;
+
+ refresh();
+
+ emit showTopLevelItemsChanged();
+ }
+}
+
+int AppsModel::appNameFormat() const
+{
+ return m_appNameFormat;
+}
+
+void AppsModel::setAppNameFormat(int format)
+{
+ if (m_appNameFormat != (AppEntry::NameFormat)format) {
+ m_appNameFormat = (AppEntry::NameFormat)format;
+
+ refresh();
+
+ emit appNameFormatChanged();
+ }
+}
+
+QObject* AppsModel::appletInterface() const
+{
+ return m_appletInterface;
+}
+
+void AppsModel::setAppletInterface(QObject* appletInterface)
+{
+ if (m_appletInterface != appletInterface) {
+ m_appletInterface = appletInterface;
+
+ refresh();
+
+ emit appletInterfaceChanged();
+ }
+}
+
+QStringList AppsModel::hiddenEntries() const
+{
+ return m_hiddenEntries;
+}
+
+void AppsModel::refresh()
+{
+ if (!m_complete) {
+ return;
+ }
+
+ if (m_staticEntryList) {
+ return;
+ }
+
+ if (rootModel() == this && !m_appletInterface) {
+ return;
+ }
+
+ beginResetModel();
+
+ refreshInternal();
+
+ endResetModel();
+
+ if (favoritesModel()) {
+ favoritesModel()->refresh();
+ }
+
+ emit countChanged();
+ emit separatorCountChanged();
+}
+
+void AppsModel::refreshInternal()
+{
+ if (m_staticEntryList) {
+ return;
+ }
+
+ if (m_entryList.count()) {
+ qDeleteAll(m_entryList);
+ m_entryList.clear();
+ emit cleared();
+ }
+
+ m_hiddenEntries.clear();
+ m_separatorCount = 0;
+
+ if (m_entryPath.isEmpty()) {
+ KServiceGroup::Ptr group = KServiceGroup::root();
+ if (!group) {
+ return;
+ }
+
+ bool sortByGenericName = (appNameFormat() == AppEntry::GenericNameOnly || appNameFormat() == AppEntry::GenericNameAndName);
+
+ KServiceGroup::List list = group->entries(true /* sorted */, true /* excludeNoDisplay */,
+ true /* allowSeparators */, sortByGenericName /* sortByGenericName */);
+
+ for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); it++) {
+ const KSycocaEntry::Ptr p = (*it);
+
+ if (p->isType(KST_KServiceGroup)) {
+ KServiceGroup::Ptr subGroup(static_cast(p.data()));
+
+ if (!subGroup->noDisplay() && subGroup->childCount() > 0) {
+ AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_paginate, m_pageSize, m_flat,
+ m_sorted, m_showSeparators, m_appNameFormat);
+ m_entryList << groupEntry;
+ }
+ } else if (p->isType(KST_KService) && m_showTopLevelItems) {
+ const KService::Ptr service(static_cast(p.data()));
+
+ if (service->noDisplay()) {
+ continue;
+ }
+
+ bool found = false;
+
+ foreach (const AbstractEntry *entry, m_entryList) {
+ if (entry->type() == AbstractEntry::RunnableType
+ && static_cast(entry)->service()->storageId() == service->storageId()) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ m_entryList << new AppEntry(this, service, m_appNameFormat);
+ }
+ } else if (p->isType(KST_KServiceSeparator) && m_showSeparators && m_showTopLevelItems) {
+ if (!m_entryList.count()) {
+ continue;
+ }
+
+ if (m_entryList.last()->type() == AbstractEntry::SeparatorType) {
+ continue;
+ }
+
+ m_entryList << new SeparatorEntry(this);
+ ++m_separatorCount;
+ }
+ }
+
+ if (m_entryList.count()) {
+ while (m_entryList.last()->type() == AbstractEntry::SeparatorType) {
+ m_entryList.removeLast();
+ --m_separatorCount;
+ }
+ }
+
+ if (m_sorted) {
+ sortEntries();
+ }
+
+ m_changeTimer = new QTimer(this);
+ m_changeTimer->setSingleShot(true);
+ m_changeTimer->setInterval(100);
+ connect(m_changeTimer, SIGNAL(timeout()), this, SLOT(refresh()));
+
+ connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), SLOT(checkSycocaChanges(QStringList)));
+ } else {
+ KServiceGroup::Ptr group = KServiceGroup::group(m_entryPath);
+ processServiceGroup(group);
+
+ if (m_entryList.count()) {
+ while (m_entryList.last()->type() == AbstractEntry::SeparatorType) {
+ m_entryList.removeLast();
+ --m_separatorCount;
+ }
+ }
+
+ if (m_sorted) {
+ sortEntries();
+ }
+
+ if (m_paginate) {
+ QList groups;
+
+ int at = 0;
+ QList page;
+
+ foreach(AbstractEntry *app, m_entryList) {
+ page.append(app);
+
+ if (at == (m_pageSize - 1)) {
+ at = 0;
+ AppsModel *model = new AppsModel(page, true, this);
+ groups.append(new GroupEntry(this, QString(), QString(), model));
+ page.clear();
+ } else {
+ ++at;
+ }
+ }
+
+ if (page.count()) {
+ AppsModel *model = new AppsModel(page, true, this);
+ groups.append(new GroupEntry(this, QString(), QString(), model));
+ }
+
+ m_entryList = groups;
+ }
+ }
+}
+
+void AppsModel::processServiceGroup(KServiceGroup::Ptr group)
+{
+ if (!group || !group->isValid()) {
+ return;
+ }
+
+ bool hasSubGroups = false;
+
+ foreach(KServiceGroup::Ptr subGroup, group->groupEntries(KServiceGroup::ExcludeNoDisplay)) {
+ if (subGroup->childCount() > 0) {
+ hasSubGroups = true;
+
+ break;
+ }
+ }
+
+ bool sortByGenericName = (appNameFormat() == AppEntry::GenericNameOnly || appNameFormat() == AppEntry::GenericNameAndName);
+
+ KServiceGroup::List list = group->entries(true /* sorted */,
+ true /* excludeNoDisplay */,
+ (!m_flat || (m_flat && !hasSubGroups)) /* allowSeparators */,
+ sortByGenericName /* sortByGenericName */);
+
+ QStringList hiddenApps;
+
+ QObject *appletInterface = rootModel()->property("appletInterface").value();
+ QQmlPropertyMap *appletConfig = nullptr;
+ if (appletInterface) {
+ appletConfig = qobject_cast(appletInterface->property("configuration").value());
+ }
+ if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications"))) {
+ hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
+ }
+
+ for (KServiceGroup::List::ConstIterator it = list.constBegin();
+ it != list.constEnd(); it++) {
+ const KSycocaEntry::Ptr p = (*it);
+
+ if (p->isType(KST_KService)) {
+ const KService::Ptr service(static_cast(p.data()));
+
+ if (service->noDisplay()) {
+ continue;
+ }
+
+ if (hiddenApps.contains(service->menuId())) {
+ m_hiddenEntries << service->menuId();
+
+ continue;
+ }
+
+ bool found = false;
+
+ foreach (const AbstractEntry *entry, m_entryList) {
+ if (entry->type() == AbstractEntry::RunnableType
+ && static_cast(entry)->service()->storageId() == service->storageId()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ m_entryList << new AppEntry(this, service, m_appNameFormat);
+ }
+ } else if (p->isType(KST_KServiceSeparator) && m_showSeparators) {
+ if (!m_entryList.count()) {
+ continue;
+ }
+
+ if (m_entryList.last()->type() == AbstractEntry::SeparatorType) {
+ continue;
+ }
+
+ m_entryList << new SeparatorEntry(this);
+ ++m_separatorCount;
+ } else if (p->isType(KST_KServiceGroup)) {
+ const KServiceGroup::Ptr subGroup(static_cast(p.data()));
+
+ if (subGroup->childCount() == 0) {
+ continue;
+ }
+
+ if (m_flat) {
+ m_sorted = true;
+ const KServiceGroup::Ptr serviceGroup(static_cast(p.data()));
+ processServiceGroup(serviceGroup);
+ } else {
+ AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_paginate, m_pageSize, m_flat,
+ m_sorted, m_showSeparators, m_appNameFormat);
+ m_entryList << groupEntry;
+ }
+ }
+ }
+}
+
+void AppsModel::sortEntries()
+{
+ QCollator c;
+
+ std::sort(m_entryList.begin(), m_entryList.end(),
+ [&c](AbstractEntry* a, AbstractEntry* b) {
+ if (a->type() != b->type()) {
+ return a->type() > b->type();
+ } else {
+ return c.compare(a->name(), b->name()) < 0;
+ }
+ });
+}
+
+void AppsModel::checkSycocaChanges(const QStringList &changes)
+{
+ if (changes.contains(QLatin1String("services")) || changes.contains(QLatin1String("apps")) || changes.contains(QLatin1String("xdgdata-apps"))) {
+ m_changeTimer->start();
+ }
+}
+
+void AppsModel::entryChanged(AbstractEntry *entry)
+{
+ int i = m_entryList.indexOf(entry);
+
+ if (i != -1) {
+ QModelIndex idx = index(i, 0);
+ emit dataChanged(idx, idx);
+ }
+}
+
+void AppsModel::classBegin()
+{
+
+}
+
+void AppsModel::componentComplete()
+{
+ m_complete = true;
+
+ if (m_autoPopulate) {
+ refresh();
+ }
+}
diff --git a/applets/kicker/plugin/computermodel.h b/applets/kicker/plugin/computermodel.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/computermodel.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef COMPUTERMODEL_H
+#define COMPUTERMODEL_H
+
+#include "forwardingmodel.h"
+#include "appentry.h"
+
+#include
+#include
+
+class SimpleFavoritesModel;
+
+class KConcatenateRowsProxyModel;
+class KFilePlacesModel;
+
+namespace Solid {
+ class Device;
+}
+
+class FilteredPlacesModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+ public:
+ explicit FilteredPlacesModel(QObject *parent = nullptr);
+ ~FilteredPlacesModel() override;
+
+ QUrl url(const QModelIndex &index) const;
+ bool isDevice(const QModelIndex &index) const;
+ Solid::Device deviceForIndex(const QModelIndex &index) const;
+
+ protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
+
+ private:
+ KFilePlacesModel *m_placesModel;
+};
+
+class RunCommandModel : public AbstractModel
+{
+ Q_OBJECT
+
+ public:
+ RunCommandModel(QObject *parent = nullptr);
+ ~RunCommandModel() override;
+
+ QString description() const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) override;
+};
+
+class ComputerModel : public ForwardingModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int appNameFormat READ appNameFormat WRITE setAppNameFormat NOTIFY appNameFormatChanged)
+ Q_PROPERTY(QObject* appletInterface READ appletInterface WRITE setAppletInterface NOTIFY appletInterfaceChanged)
+ Q_PROPERTY(QStringList systemApplications READ systemApplications WRITE setSystemApplications NOTIFY systemApplicationsChanged)
+
+ public:
+ explicit ComputerModel(QObject *parent = nullptr);
+ ~ComputerModel() override;
+
+ QString description() const override;
+
+ int appNameFormat() const;
+ void setAppNameFormat(int format);
+
+ QObject *appletInterface() const;
+ void setAppletInterface(QObject *appletInterface);
+
+ QStringList systemApplications() const;
+ void setSystemApplications(const QStringList &apps);
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) override;
+
+ Q_SIGNALS:
+ void appNameFormatChanged() const;
+ void appletInterfaceChanged() const;
+ void systemApplicationsChanged() const;
+
+ private Q_SLOTS:
+ void onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi);
+
+ private:
+ KConcatenateRowsProxyModel *m_concatProxy;
+ RunCommandModel *m_runCommandModel;
+ SimpleFavoritesModel *m_systemAppsModel;
+ FilteredPlacesModel *m_filteredPlacesModel;
+ AppEntry::NameFormat m_appNameFormat;
+ QObject *m_appletInterface;
+};
+
+#endif
diff --git a/applets/kicker/plugin/computermodel.cpp b/applets/kicker/plugin/computermodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/computermodel.cpp
@@ -0,0 +1,298 @@
+/***************************************************************************
+ * Copyright (C) 2007 Kevin Ottens *
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "computermodel.h"
+#include "actionlist.h"
+#include "simplefavoritesmodel.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "krunner_interface.h"
+
+FilteredPlacesModel::FilteredPlacesModel(QObject *parent) : QSortFilterProxyModel(parent)
+, m_placesModel(new KFilePlacesModel(this))
+{
+ setSourceModel(m_placesModel);
+ sort(0);
+}
+
+FilteredPlacesModel::~FilteredPlacesModel()
+{
+}
+
+QUrl FilteredPlacesModel::url(const QModelIndex &index) const
+{
+ return KFilePlacesModel::convertedUrl(m_placesModel->url(mapToSource(index)));
+}
+
+bool FilteredPlacesModel::isDevice(const QModelIndex &index) const
+{
+ return m_placesModel->isDevice(mapToSource(index));
+}
+
+Solid::Device FilteredPlacesModel::deviceForIndex(const QModelIndex &index) const
+{
+ return m_placesModel->deviceForIndex(mapToSource(index));
+}
+
+bool FilteredPlacesModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ const QModelIndex index = m_placesModel->index(sourceRow, 0, sourceParent);
+
+ return !m_placesModel->isHidden(index)
+ && !m_placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool();
+}
+
+bool FilteredPlacesModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ bool lDevice = m_placesModel->isDevice(left);
+ bool rDevice = m_placesModel->isDevice(right);
+
+ if (lDevice && !rDevice) {
+ return false;
+ } else if (!lDevice && rDevice) {
+ return true;
+ }
+
+ return (left.row() < right.row());
+}
+
+RunCommandModel::RunCommandModel(QObject *parent) : AbstractModel(parent)
+{
+}
+
+RunCommandModel::~RunCommandModel()
+{
+}
+
+QString RunCommandModel::description() const
+{
+ return QString();
+}
+
+QVariant RunCommandModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ if (role == Qt::DisplayRole) {
+ return i18n("Show KRunner");
+ } else if (role == Qt::DecorationRole) {
+ return QIcon::fromTheme(QStringLiteral("plasma-search"));
+ } else if (role == Kicker::DescriptionRole) {
+ return i18n("Search, calculate, or run a command");
+ } else if (role == Kicker::GroupRole) {
+ return i18n("Applications");
+ }
+
+ return QVariant();
+}
+
+int RunCommandModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : (KAuthorized::authorize(QStringLiteral("run_command")) ? 1 : 0);
+}
+
+Q_INVOKABLE bool RunCommandModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ Q_UNUSED(actionId)
+ Q_UNUSED(argument)
+
+ if (row == 0 && KAuthorized::authorize(QStringLiteral("run_command"))) {
+ org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"),
+ QStringLiteral("/App"), QDBusConnection::sessionBus());
+ krunner.display();
+
+ return true;
+ }
+
+ return false;
+}
+
+ComputerModel::ComputerModel(QObject *parent) : ForwardingModel(parent)
+, m_concatProxy(new KConcatenateRowsProxyModel(this))
+, m_runCommandModel(new RunCommandModel(this))
+, m_systemAppsModel(new SimpleFavoritesModel(this))
+, m_filteredPlacesModel(new FilteredPlacesModel(this))
+, m_appNameFormat(AppEntry::NameOnly)
+, m_appletInterface(nullptr)
+{
+ connect(m_systemAppsModel, &SimpleFavoritesModel::favoritesChanged, this, &ComputerModel::systemApplicationsChanged);
+ m_systemAppsModel->setFavorites(QStringList() << QStringLiteral("systemsettings.desktop"));
+
+ m_concatProxy->addSourceModel(m_runCommandModel);
+ m_concatProxy->addSourceModel(m_systemAppsModel);
+ m_concatProxy->addSourceModel(m_filteredPlacesModel);
+
+ setSourceModel(m_concatProxy);
+}
+
+ComputerModel::~ComputerModel()
+{
+}
+
+QString ComputerModel::description() const
+{
+ return i18n("Computer");
+}
+
+int ComputerModel::appNameFormat() const
+{
+ return m_appNameFormat;
+}
+
+void ComputerModel::setAppNameFormat(int format)
+{
+ if (m_appNameFormat != (AppEntry::NameFormat)format) {
+ m_appNameFormat = (AppEntry::NameFormat)format;
+
+ m_systemAppsModel->refresh();
+
+ emit appNameFormatChanged();
+ }
+}
+
+QObject *ComputerModel::appletInterface() const
+{
+ return m_appletInterface;
+}
+
+void ComputerModel::setAppletInterface(QObject *appletInterface)
+{
+ if (m_appletInterface != appletInterface) {
+ m_appletInterface = appletInterface;
+
+ emit appletInterfaceChanged();
+ }
+}
+
+QStringList ComputerModel::systemApplications() const
+{
+ return m_systemAppsModel->favorites();
+}
+
+void ComputerModel::setSystemApplications(const QStringList &apps)
+{
+ m_systemAppsModel->setFavorites(apps);
+}
+
+QVariant ComputerModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(index.row(),
+ index.column()));
+
+ bool isPlace = (sourceIndex.model() == m_filteredPlacesModel);
+
+ if (isPlace) {
+ if (role == Kicker::DescriptionRole) {
+ if (m_filteredPlacesModel->isDevice(sourceIndex)) {
+ Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex);
+ Solid::StorageAccess *access = device.as();
+
+ if (access) {
+ return access->filePath();
+ } else {
+ return QString();
+ }
+ }
+ } else if (role == Kicker::FavoriteIdRole) {
+ if (!m_filteredPlacesModel->isDevice(sourceIndex)) {
+ return m_filteredPlacesModel->url(sourceIndex);
+ }
+ } else if (role == Kicker::UrlRole) {
+ return m_filteredPlacesModel->url(sourceIndex);
+ } else if (role == Kicker::GroupRole) {
+ return sourceIndex.data(KFilePlacesModel::GroupRole).toString();
+ } else if (role == Qt::DisplayRole || role == Qt::DecorationRole) {
+ return sourceIndex.data(role);
+ }
+ } else if (role == Kicker::GroupRole) {
+ return i18n("Applications");
+ } else {
+ return sourceIndex.data(role);
+ }
+
+ return QVariant();
+}
+
+bool ComputerModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(row, 0));
+
+ if (sourceIndex.model() == m_filteredPlacesModel) {
+ const QUrl &url = m_filteredPlacesModel->url(sourceIndex);
+
+ if (url.isValid()) {
+ new KRun(url, nullptr);
+
+ return true;
+ }
+
+ Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex);
+ Solid::StorageAccess *access = device.as();
+
+ if (access && !access->isAccessible()) {
+ connect(access, &Solid::StorageAccess::setupDone, this, &ComputerModel::onSetupDone);
+ access->setup();
+
+ return true;
+ }
+ } else {
+ AbstractModel *model = nullptr;
+
+ if (sourceIndex.model() == m_systemAppsModel) {
+ model = m_systemAppsModel;
+ } else {
+ model = m_runCommandModel;
+ }
+
+ return model->trigger(sourceIndex.row(), actionId, argument);
+ }
+
+ return false;
+}
+
+void ComputerModel::onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi)
+{
+ Q_UNUSED(errorData);
+
+ if (error != Solid::NoError) {
+ return;
+ }
+
+ Solid::Device device(udi);
+ Solid::StorageAccess *access = device.as();
+
+ Q_ASSERT(access);
+
+ new KRun(QUrl::fromLocalFile(access->filePath()), nullptr);
+}
diff --git a/dataengines/share/packagestructure/share_package.cpp b/applets/kicker/plugin/contactentry.h
rename from dataengines/share/packagestructure/share_package.cpp
rename to applets/kicker/plugin/contactentry.h
--- a/dataengines/share/packagestructure/share_package.cpp
+++ b/applets/kicker/plugin/contactentry.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2010 Artur Duque de Souza *
+ * Copyright (C) 201 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,29 +17,39 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#include
-#include
+#ifndef CONTACTENTRY_H
+#define CONTACTENTRY_H
-#include "share_package.h"
-#include "config-workspace.h"
+#include "abstractentry.h"
-SharePackage::SharePackage(QObject *, QVariantList)
-{
+namespace KPeople {
+ class PersonData;
}
-void SharePackage::initPackage(KPackage::Package* package)
+class ContactEntry : public AbstractEntry
{
-// KPackage::PackageStructure::initPackage(package);
- package->addDirectoryDefinition("scripts", QStringLiteral("code"), i18n("Executable Scripts"));
- QStringList mimetypes;
- mimetypes << QStringLiteral("text/*");
- package->setMimeTypes( "scripts", mimetypes );
-
- package->addFileDefinition("mainscript", QStringLiteral("code/main.js"), i18n("Main Script File"));
- package->setDefaultPackageRoot(PLASMA_RELATIVE_DATA_INSTALL_DIR "shareprovider/");
-}
+ public:
+ explicit ContactEntry(AbstractModel *owner, const QString &id);
+
+ EntryType type() const override { return RunnableType; }
+
+ bool isValid() const override;
+
+ QIcon icon() const override;
+ QString name() const override;
+
+ QString id() const override;
+ QUrl url() const override;
+
+ bool hasActions() const override;
+ QVariantList actions() const override;
+
+ bool run(const QString& actionId = QString(), const QVariant &argument = QVariant()) override;
-K_EXPORT_KPACKAGE_PACKAGE_WITH_JSON(SharePackage, "plasma-packagestructure-share.json")
+ static void showPersonDetailsDialog(const QString &id);
-#include "share_package.moc"
+ private:
+ KPeople::PersonData *m_personData;
+};
+#endif
diff --git a/applets/kicker/plugin/contactentry.cpp b/applets/kicker/plugin/contactentry.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/contactentry.cpp
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * Copyright (C) 201 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "contactentry.h"
+#include "actionlist.h"
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+ContactEntry::ContactEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner)
+, m_personData(nullptr)
+{
+ if (!id.isEmpty()) {
+ m_personData = new KPeople::PersonData(id);
+
+ QObject::connect(m_personData, &KPeople::PersonData::dataChanged,
+ [this] { if (m_owner) { m_owner->entryChanged(this); } }
+ );
+ }
+}
+
+bool ContactEntry::isValid() const {
+ return m_personData;
+}
+
+QIcon ContactEntry::icon() const
+{
+ if (m_personData) {
+ QPixmap photo = m_personData->photo();
+ QBitmap mask(photo.size());
+ QPainter painter(&mask);
+ mask.fill(Qt::white);
+ painter.setBrush(Qt::black);
+ painter.drawEllipse(0, 0, mask.width(), mask.height());
+ photo.setMask(mask);
+
+ photo = photo.scaled(m_owner->iconSize(), m_owner->iconSize(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+
+ KIconLoader::global()->drawOverlays(QStringList() << m_personData->presenceIconName(), photo, KIconLoader::Panel);
+
+ return QIcon(photo);
+ }
+
+ return QIcon::fromTheme(QStringLiteral("unknown"));
+}
+
+QString ContactEntry::name() const
+{
+ if (m_personData) {
+ return m_personData->name();
+ }
+
+ return QString();
+}
+
+QString ContactEntry::id() const
+{
+ if (m_personData) {
+ const QString &id = m_personData->personUri();
+
+ if (id.isEmpty()) {
+ const QStringList uris = m_personData->contactUris();
+
+ if (!uris.isEmpty()) {
+ return uris.at(0);
+ }
+ } else {
+ return id;
+ }
+ }
+
+ return QString();
+}
+
+QUrl ContactEntry::url() const
+{
+ if (m_personData) {
+ return QUrl(m_personData->personUri());
+ }
+
+ return QUrl();
+}
+
+bool ContactEntry::hasActions() const
+{
+ return m_personData;
+}
+
+QVariantList ContactEntry::actions() const
+{
+ QVariantList actionList;
+
+ actionList << Kicker::createActionItem(i18n("Show Contact Information..."), QString("identity"), QStringLiteral("showContactInfo"));
+
+ return actionList;
+}
+
+bool ContactEntry::run(const QString& actionId, const QVariant &argument)
+{
+ Q_UNUSED(argument)
+
+ if (!m_personData) {
+ return false;
+ }
+
+ if (actionId == QLatin1String("showContactInfo")) {
+ showPersonDetailsDialog(m_personData->personUri());
+ }
+
+ return false;
+}
+
+void ContactEntry::showPersonDetailsDialog(const QString &id) {
+ KPeople::PersonDetailsDialog *view = new KPeople::PersonDetailsDialog(nullptr);
+ KPeople::PersonData *data = new KPeople::PersonData(id, view);
+ view->setPerson(data);
+ view->setAttribute(Qt::WA_DeleteOnClose);
+ view->show();
+}
diff --git a/dataengines/share/shareservice.h b/applets/kicker/plugin/containmentinterface.h
copy from dataengines/share/shareservice.h
copy to applets/kicker/plugin/containmentinterface.h
--- a/dataengines/share/shareservice.h
+++ b/applets/kicker/plugin/containmentinterface.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2010 Artur Duque de Souza *
+ * Copyright (C) 2014 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,54 +17,42 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#ifndef SHARE_SERVICE_H
-#define SHARE_SERVICE_H
+#ifndef CONTAINMENTINTERFACE_H
+#define CONTAINMENTINTERFACE_H
-#include "shareengine.h"
-
-#include
-#include
-#include
-
-class ShareProvider;
+#include
+#include
namespace Plasma {
- class ServiceJob;
+ class Containment;
}
-namespace KJSEmbed {
- class Engine;
-}
-
-class ShareService : public Plasma::Service
+class ContainmentInterface : public QObject
{
Q_OBJECT
-public:
- explicit ShareService(ShareEngine *engine);
- Plasma::ServiceJob *createJob(const QString &operation,
- QMap ¶meters) override;
-};
+ public:
+ enum Target {
+ Desktop = 0,
+ Panel,
+ TaskManager
+ };
-class ShareJob : public Plasma::ServiceJob
-{
- Q_OBJECT
+ Q_ENUM(Target)
+
+ explicit ContainmentInterface(QObject *parent = nullptr);
+ ~ContainmentInterface() override;
+
+ static Q_INVOKABLE bool mayAddLauncher(QObject *appletInterface, Target target, const QString &entryPath = QString());
-public:
- ShareJob(const QString &destination, const QString &operation,
- QMap ¶meters, QObject *parent = nullptr);
- ~ShareJob() override;
- void start() override;
+ static Q_INVOKABLE void addLauncher(QObject *appletInterface, Target target, const QString &entryPath);
-public Q_SLOTS:
- void publish();
- void showResult(const QString &url);
- void showError(const QString &msg);
+ static Q_INVOKABLE QObject* screenContainment(QObject *appletInterface);
+ static Q_INVOKABLE bool screenContainmentMutable(QObject *appletInterface);
+ static Q_INVOKABLE void ensureMutable(Plasma::Containment *containment);
-private:
- QScopedPointer m_engine;
- ShareProvider *m_provider;
- KPackage::Package m_package;
+ private:
+ static QStringList m_knownTaskManagers;
};
-#endif // SHARE_SERVICE
+#endif
diff --git a/applets/kicker/plugin/containmentinterface.cpp b/applets/kicker/plugin/containmentinterface.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/containmentinterface.cpp
@@ -0,0 +1,252 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "containmentinterface.h"
+
+#include
+#include
+#include
+
+#include
+
+#include
+
+// FIXME HACK TODO: Unfortunately we have no choice but to hard-code a list of
+// applets we know to expose the correct interface right now -- this is slated
+// for replacement with some form of generic service.
+QStringList ContainmentInterface::m_knownTaskManagers = QStringList() << QLatin1String("org.kde.plasma.taskmanager")
+ << QLatin1String("org.kde.plasma.icontasks")
+ << QLatin1String("org.kde.plasma.expandingiconstaskmanager");
+
+ContainmentInterface::ContainmentInterface(QObject *parent) : QObject(parent)
+{
+}
+
+ContainmentInterface::~ContainmentInterface()
+{
+}
+
+bool ContainmentInterface::mayAddLauncher(QObject *appletInterface, ContainmentInterface::Target target, const QString &entryPath)
+{
+ if (!appletInterface) {
+ return false;
+ }
+
+ Plasma::Applet *applet = appletInterface->property("_plasma_applet").value();
+ Plasma::Containment *containment = applet->containment();
+
+ if (!containment) {
+ return false;
+ }
+
+ Plasma::Corona *corona = containment->corona();
+
+ if (!corona) {
+ return false;
+ }
+
+ switch (target) {
+ case Desktop: {
+ containment = corona->containmentForScreen(containment->screen());
+
+ if (containment) {
+ return (containment->immutability() == Plasma::Types::Mutable);
+ }
+
+ break;
+ }
+ case Panel: {
+ if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.panel"))
+ {
+ return (containment->immutability() == Plasma::Types::Mutable);
+ }
+
+ break;
+ }
+ case TaskManager: {
+ if (!entryPath.isEmpty() && containment->pluginMetaData().pluginId() == QLatin1String("org.kde.panel"))
+ {
+ const Plasma::Applet *taskManager = nullptr;
+
+ foreach(const Plasma::Applet *applet, containment->applets()) {
+ if (m_knownTaskManagers.contains(applet->pluginMetaData().pluginId())) {
+ taskManager = applet;
+
+ break;
+ }
+ }
+
+ if (taskManager) {
+ QQuickItem* gObj = qobject_cast(taskManager->property("_plasma_graphicObject").value());
+
+ if (!gObj || !gObj->childItems().count()) {
+ return false;
+ }
+
+ QQuickItem *rootItem = gObj->childItems().first();
+
+ QVariant ret;
+
+ QMetaObject::invokeMethod(rootItem, "hasLauncher", Q_RETURN_ARG(QVariant, ret),
+ Q_ARG(QVariant, QUrl::fromLocalFile(entryPath)));
+
+ return !ret.toBool();
+ }
+ }
+
+ break;
+ }
+ }
+
+ return false;
+}
+
+void ContainmentInterface::addLauncher(QObject *appletInterface, ContainmentInterface::Target target, const QString &entryPath)
+{
+ if (!appletInterface) {
+ return;
+ }
+
+ Plasma::Applet *applet = appletInterface->property("_plasma_applet").value();
+ Plasma::Containment *containment = applet->containment();
+
+ if (!containment) {
+ return;
+ }
+
+ Plasma::Corona *corona = containment->corona();
+
+ if (!corona) {
+ return;
+ }
+
+ switch (target) {
+ case Desktop: {
+ containment = corona->containmentForScreen(containment->screen());
+
+ if (!containment) {
+ return;
+ }
+
+ const QStringList &containmentProvides = KPluginMetaData::readStringList(containment->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides"));
+
+ if (containmentProvides.contains(QLatin1String("org.kde.plasma.filemanagement"))) {
+ QQuickItem* gObj = qobject_cast(containment->property("_plasma_graphicObject").value());
+
+ if (!gObj || !gObj->childItems().count()) {
+ return;
+ }
+
+ QQuickItem *rootItem = nullptr;
+
+ foreach(QQuickItem *item, gObj->childItems()) {
+ if (item->objectName() == QLatin1String("folder")) {
+ rootItem = item;
+
+ break;
+ }
+ }
+
+ if (rootItem) {
+ QMetaObject::invokeMethod(rootItem, "addLauncher", Q_ARG(QVariant, QUrl::fromLocalFile(entryPath)));
+ }
+ } else {
+ containment->createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList() << entryPath);
+ }
+
+ break;
+ }
+ case Panel: {
+ if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.panel"))
+ {
+ containment->createApplet(QStringLiteral("org.kde.plasma.icon"), QVariantList() << entryPath);
+ }
+
+ break;
+ }
+ case TaskManager: {
+ if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.panel"))
+ {
+ const Plasma::Applet *taskManager = nullptr;
+
+ foreach(const Plasma::Applet *applet, containment->applets()) {
+ if (m_knownTaskManagers.contains(applet->pluginMetaData().pluginId())) {
+ taskManager = applet;
+
+ break;
+ }
+ }
+
+ if (taskManager) {
+ QQuickItem* gObj = qobject_cast(taskManager->property("_plasma_graphicObject").value());
+
+ if (!gObj || !gObj->childItems().count()) {
+ return;
+ }
+
+ QQuickItem *rootItem = gObj->childItems().first();
+
+ QMetaObject::invokeMethod(rootItem, "addLauncher", Q_ARG(QVariant, QUrl::fromLocalFile(entryPath)));
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+QObject* ContainmentInterface::screenContainment(QObject *appletInterface)
+{
+ if (!appletInterface) {
+ return nullptr;
+ }
+
+ const Plasma::Applet *applet = appletInterface->property("_plasma_applet").value();
+ Plasma::Containment *containment = applet->containment();
+
+ if (!containment) {
+ return nullptr;
+ }
+
+ Plasma::Corona *corona = containment->corona();
+
+ if (!corona) {
+ return nullptr;
+ }
+
+ return corona->containmentForScreen(containment->screen());
+}
+
+bool ContainmentInterface::screenContainmentMutable(QObject *appletInterface)
+{
+ const Plasma::Containment *containment = static_cast(screenContainment(appletInterface));
+
+ if (containment) {
+ return (containment->immutability() == Plasma::Types::Mutable);
+ }
+
+ return false;
+}
+
+void ContainmentInterface::ensureMutable(Plasma::Containment *containment)
+{
+ if (containment && containment->immutability() != Plasma::Types::Mutable) {
+ containment->actions()->action(QStringLiteral("lock widgets"))->trigger();
+ }
+}
diff --git a/applets/kicker/plugin/dashboardwindow.h b/applets/kicker/plugin/dashboardwindow.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/dashboardwindow.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef DASHBOARDWINDOW_H
+#define DASHBOARDWINDOW_H
+
+#include
+
+#include
+#include
+
+class DashboardWindow : public QQuickWindow
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickItem* mainItem READ mainItem WRITE setMainItem NOTIFY mainItemChanged)
+ Q_PROPERTY(QQuickItem* visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged)
+ Q_PROPERTY(QQuickItem* keyEventProxy READ keyEventProxy WRITE setKeyEventProxy NOTIFY keyEventProxyChanged)
+ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged)
+
+ Q_CLASSINFO("DefaultProperty", "mainItem")
+
+ public:
+ explicit DashboardWindow(QQuickItem *parent = nullptr);
+ ~DashboardWindow() override;
+
+ QQuickItem *mainItem() const;
+ void setMainItem(QQuickItem *item);
+
+ QQuickItem *visualParent() const;
+ void setVisualParent(QQuickItem *item);
+
+ QQuickItem *keyEventProxy() const;
+ void setKeyEventProxy(QQuickItem *item);
+
+ QColor backgroundColor() const;
+ void setBackgroundColor(const QColor &color);
+
+ Q_INVOKABLE void toggle();
+
+ Q_SIGNALS:
+ void mainItemChanged() const;
+ void visualParentChanged() const;
+ void keyEventProxyChanged() const;
+ void backgroundColorChanged() const;
+ void keyEscapePressed() const;
+
+ private Q_SLOTS:
+ void updateTheme();
+ void visualParentWindowChanged(QQuickWindow *window);
+ void visualParentScreenChanged(QScreen *screen);
+
+ protected:
+ bool event(QEvent *event) override;
+ void keyPressEvent(QKeyEvent *e) override;
+
+ private:
+ QQuickItem *m_mainItem;
+ QPointer m_visualParentItem;
+ QPointer m_visualParentWindow;
+ QPointer m_keyEventProxy;
+ Plasma::Theme m_theme;
+};
+
+#endif
diff --git a/applets/kicker/plugin/dashboardwindow.cpp b/applets/kicker/plugin/dashboardwindow.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/dashboardwindow.cpp
@@ -0,0 +1,235 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "dashboardwindow.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+DashboardWindow::DashboardWindow(QQuickItem *parent) : QQuickWindow(parent ? parent->window() : nullptr)
+, m_mainItem(nullptr)
+, m_visualParentItem(nullptr)
+, m_visualParentWindow(nullptr)
+{
+ setClearBeforeRendering(true);
+ setFlags(Qt::FramelessWindowHint);
+
+ setIcon(QIcon::fromTheme(QStringLiteral("plasma")));
+
+ connect(&m_theme, &Plasma::Theme::themeChanged, this, &DashboardWindow::updateTheme);
+}
+
+DashboardWindow::~DashboardWindow()
+{
+}
+
+QQuickItem *DashboardWindow::mainItem() const
+{
+ return m_mainItem;
+}
+
+void DashboardWindow::setMainItem(QQuickItem *item)
+{
+ if (m_mainItem != item) {
+ if (m_mainItem) {
+ m_mainItem->setVisible(false);
+ }
+
+ m_mainItem = item;
+
+ if (m_mainItem) {
+ m_mainItem->setVisible(isVisible());
+ m_mainItem->setParentItem(contentItem());
+ }
+
+ emit mainItemChanged();
+ }
+}
+
+QQuickItem *DashboardWindow::visualParent() const
+{
+ return m_visualParentItem;
+}
+
+void DashboardWindow::setVisualParent(QQuickItem *item)
+{
+ if (m_visualParentItem != item) {
+ if (m_visualParentItem) {
+ disconnect(m_visualParentItem.data(), &QQuickItem::windowChanged, this, &DashboardWindow::visualParentWindowChanged);
+ }
+
+ m_visualParentItem = item;
+
+ if (m_visualParentItem) {
+ if (m_visualParentItem->window()) {
+ visualParentWindowChanged(m_visualParentItem->window());
+ }
+
+ connect(m_visualParentItem.data(), &QQuickItem::windowChanged, this, &DashboardWindow::visualParentWindowChanged);
+ }
+
+ emit visualParentChanged();
+ }
+}
+
+QColor DashboardWindow::backgroundColor() const
+{
+ return color();
+}
+
+void DashboardWindow::setBackgroundColor(const QColor &c)
+{
+ if (color() != c) {
+ setColor(c);
+
+ emit backgroundColorChanged();
+ }
+}
+
+QQuickItem *DashboardWindow::keyEventProxy() const
+{
+ return m_keyEventProxy;
+}
+
+void DashboardWindow::setKeyEventProxy(QQuickItem *item)
+{
+ if (m_keyEventProxy != item) {
+ m_keyEventProxy = item;
+
+ emit keyEventProxyChanged();
+ }
+}
+
+void DashboardWindow::toggle() {
+ if (isVisible()) {
+ close();
+ } else {
+ resize(screen()->size());
+ showFullScreen();
+ KWindowSystem::forceActiveWindow(winId());
+ }
+}
+
+bool DashboardWindow::event(QEvent *event)
+{
+ if (event->type() == QEvent::Expose) {
+ // FIXME TODO: We can remove this once we depend on Qt 5.6.1+.
+ // See: https://bugreports.qt.io/browse/QTBUG-26978
+ KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
+ } else if (event->type() == QEvent::PlatformSurface) {
+ const QPlatformSurfaceEvent *pSEvent = static_cast(event);
+
+ if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
+ KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
+ }
+ } else if (event->type() == QEvent::Show) {
+ updateTheme();
+
+ if (m_mainItem) {
+ m_mainItem->setVisible(true);
+ }
+ } else if (event->type() == QEvent::Hide) {
+ if (m_mainItem) {
+ m_mainItem->setVisible(false);
+ }
+ } else if (event->type() == QEvent::FocusOut) {
+ if (isVisible()) {
+ KWindowSystem::raiseWindow(winId());
+ KWindowSystem::forceActiveWindow(winId());
+ }
+ }
+
+ return QQuickWindow::event(event);
+}
+
+void DashboardWindow::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key() == Qt::Key_Escape) {
+ emit keyEscapePressed();
+
+ return;
+ } else if (m_keyEventProxy && !m_keyEventProxy->hasActiveFocus()
+ && !(e->key() == Qt::Key_Home)
+ && !(e->key() == Qt::Key_End)
+ && !(e->key() == Qt::Key_Left)
+ && !(e->key() == Qt::Key_Up)
+ && !(e->key() == Qt::Key_Right)
+ && !(e->key() == Qt::Key_Down)
+ && !(e->key() == Qt::Key_PageUp)
+ && !(e->key() == Qt::Key_PageDown)
+ && !(e->key() == Qt::Key_Enter)
+ && !(e->key() == Qt::Key_Return)
+ && !(e->key() == Qt::Key_Menu)
+ && !(e->key() == Qt::Key_Tab)
+ && !(e->key() == Qt::Key_Backtab)) {
+
+ QPointer previousFocusItem = activeFocusItem();
+
+ m_keyEventProxy->forceActiveFocus();
+ QEvent* eventCopy = new QKeyEvent(e->type(), e->key(), e->modifiers(),
+ e->nativeScanCode(), e->nativeVirtualKey(), e->nativeModifiers(),
+ e->text(), e->isAutoRepeat(), e->count());
+ QCoreApplication::postEvent(this, eventCopy);
+
+ // We _need_ to do it twice to make sure the event ping-pong needed
+ // for delivery happens before we sap focus again.
+ QCoreApplication::processEvents();
+ QCoreApplication::processEvents();
+
+ if (previousFocusItem) {
+ previousFocusItem->forceActiveFocus();
+ }
+
+ return;
+ }
+
+ QQuickWindow::keyPressEvent(e);
+}
+
+void DashboardWindow::updateTheme()
+{
+ KWindowEffects::enableBlurBehind(winId(), true);
+}
+
+void DashboardWindow::visualParentWindowChanged(QQuickWindow *window)
+{
+ if (m_visualParentWindow) {
+ disconnect(m_visualParentWindow.data(), &QQuickWindow::screenChanged, this, &DashboardWindow::visualParentScreenChanged);
+ }
+
+ m_visualParentWindow = window;
+
+ if (m_visualParentWindow) {
+ visualParentScreenChanged(m_visualParentWindow->screen());
+
+ connect(m_visualParentWindow.data(), &QQuickWindow::screenChanged, this, &DashboardWindow::visualParentScreenChanged);
+ }
+}
+
+void DashboardWindow::visualParentScreenChanged(QScreen *screen)
+{
+ if (screen) {
+ setScreen(screen);
+ setGeometry(screen->geometry());
+ }
+}
diff --git a/dataengines/share/shareservice.h b/applets/kicker/plugin/draghelper.h
copy from dataengines/share/shareservice.h
copy to applets/kicker/plugin/draghelper.h
--- a/dataengines/share/shareservice.h
+++ b/applets/kicker/plugin/draghelper.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2010 Artur Duque de Souza *
+ * Copyright (C) 2013 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,54 +17,44 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#ifndef SHARE_SERVICE_H
-#define SHARE_SERVICE_H
+#ifndef DRAGHELPER_H
+#define DRAGHELPER_H
-#include "shareengine.h"
+#include
+#include
+#include
-#include
-#include
-#include
+class QQuickItem;
-class ShareProvider;
-
-namespace Plasma {
- class ServiceJob;
-}
-
-namespace KJSEmbed {
- class Engine;
-}
-
-class ShareService : public Plasma::Service
+class DragHelper : public QObject
{
Q_OBJECT
-
-public:
- explicit ShareService(ShareEngine *engine);
- Plasma::ServiceJob *createJob(const QString &operation,
- QMap ¶meters) override;
-};
-
-class ShareJob : public Plasma::ServiceJob
-{
- Q_OBJECT
-
-public:
- ShareJob(const QString &destination, const QString &operation,
- QMap ¶meters, QObject *parent = nullptr);
- ~ShareJob() override;
- void start() override;
-
-public Q_SLOTS:
- void publish();
- void showResult(const QString &url);
- void showError(const QString &msg);
-
-private:
- QScopedPointer m_engine;
- ShareProvider *m_provider;
- KPackage::Package m_package;
+ Q_PROPERTY(int dragIconSize READ dragIconSize WRITE setDragIconSize NOTIFY dragIconSizeChanged)
+ Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged)
+
+ public:
+ explicit DragHelper(QObject *parent = nullptr);
+ ~DragHelper() override;
+
+ int dragIconSize() const;
+ void setDragIconSize(int size);
+ bool isDragging() const { return m_dragging; }
+
+ Q_INVOKABLE bool isDrag(int oldX, int oldY, int newX, int newY) const;
+ Q_INVOKABLE void startDrag(QQuickItem* item, const QUrl &url = QUrl(), const QIcon &icon = QIcon(),
+ const QString &extraMimeType = QString(), const QString &extraMimeData = QString());
+
+ Q_SIGNALS:
+ void dragIconSizeChanged() const;
+ void dropped() const;
+ void draggingChanged() const;
+
+ private:
+ int m_dragIconSize;
+ bool m_dragging;
+ Q_INVOKABLE void doDrag(QQuickItem* item, const QUrl &url = QUrl(), const QIcon &icon = QIcon(),
+ const QString &extraMimeType = QString(), const QString &extraMimeData = QString());
+ void setDragging(bool dragging);
};
-#endif // SHARE_SERVICE
+#endif
diff --git a/applets/kicker/plugin/draghelper.cpp b/applets/kicker/plugin/draghelper.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/draghelper.cpp
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2013 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "draghelper.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+DragHelper::DragHelper(QObject* parent) : QObject(parent)
+, m_dragIconSize(32)
+, m_dragging(false)
+{
+}
+
+DragHelper::~DragHelper()
+{
+}
+
+int DragHelper::dragIconSize() const
+{
+ return m_dragIconSize;
+}
+
+void DragHelper::setDragIconSize(int size)
+{
+ if (m_dragIconSize != size) {
+ m_dragIconSize = size;
+
+ emit dragIconSizeChanged();
+ }
+}
+
+bool DragHelper::isDrag(int oldX, int oldY, int newX, int newY) const
+{
+ return ((QPoint(oldX, oldY) - QPoint(newX, newY)).manhattanLength() >= QApplication::startDragDistance());
+}
+
+void DragHelper::startDrag(QQuickItem *item, const QUrl &url, const QIcon &icon,
+ const QString &extraMimeType, const QString &extraMimeData)
+{
+ // This allows the caller to return, making sure we don't crash if
+ // the caller is destroyed mid-drag (as can happen due to a sycoca
+ // change).
+
+ QMetaObject::invokeMethod(this, "doDrag", Qt::QueuedConnection,
+ Q_ARG(QQuickItem*, item), Q_ARG(QUrl, url), Q_ARG(QIcon, icon),
+ Q_ARG(QString, extraMimeType), Q_ARG(QString, extraMimeData));
+}
+
+void DragHelper::doDrag(QQuickItem *item, const QUrl &url, const QIcon &icon,
+ const QString &extraMimeType, const QString &extraMimeData)
+{
+ setDragging(true);
+
+ if (item && item->window() && item->window()->mouseGrabberItem()) {
+ item->window()->mouseGrabberItem()->ungrabMouse();
+ }
+
+ QDrag *drag = new QDrag(item);
+
+ QMimeData *mimeData = new QMimeData();
+
+ if (!url.isEmpty()) {
+ mimeData->setUrls(QList() << url);
+ }
+
+ if (!extraMimeType.isEmpty() && !extraMimeData.isEmpty()) {
+ mimeData->setData(extraMimeType, extraMimeData.toLatin1());
+ }
+
+ drag->setMimeData(mimeData);
+
+ if (!icon.isNull()) {
+ drag->setPixmap(icon.pixmap(m_dragIconSize, m_dragIconSize));
+ }
+
+ drag->exec();
+
+ emit dropped();
+
+ // Ensure dragging is still true when onRelease is called.
+ QTimer::singleShot(0, qApp, [this] {
+ setDragging(false);
+ });
+}
+
+void DragHelper::setDragging(bool dragging)
+{
+ if (m_dragging == dragging)
+ return;
+ m_dragging = dragging;
+ emit draggingChanged();
+}
diff --git a/applets/lock_logout/contents/config/config.qml b/applets/kicker/plugin/fileentry.h
copy from applets/lock_logout/contents/config/config.qml
copy to applets/kicker/plugin/fileentry.h
--- a/applets/lock_logout/contents/config/config.qml
+++ b/applets/kicker/plugin/fileentry.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2013 by Sebastian Kügler *
+ * Copyright (C) 2015 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,14 +17,37 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-import QtQuick 2.0
+#ifndef FILEENTRY_H
+#define FILEENTRY_H
-import org.kde.plasma.configuration 2.0
+#include "abstractentry.h"
-ConfigModel {
- ConfigCategory {
- name: i18n("General")
- icon: "system-shutdown"
- source: "ConfigGeneral.qml"
- }
-}
+class KFileItem;
+
+class FileEntry : public AbstractEntry
+{
+ public:
+ explicit FileEntry(AbstractModel *owner, const QUrl &url);
+ ~FileEntry() override;
+
+ EntryType type() const override { return RunnableType; }
+
+ bool isValid() const override;
+
+ QIcon icon() const override;
+ QString name() const override;
+ QString description() const override;
+
+ QString id() const override;
+ QUrl url() const override;
+
+ bool hasActions() const override;
+ QVariantList actions() const override;
+
+ bool run(const QString& actionId = QString(), const QVariant &argument = QVariant()) override;
+
+ private:
+ KFileItem *m_fileItem;
+};
+
+#endif
diff --git a/applets/kicker/plugin/fileentry.cpp b/applets/kicker/plugin/fileentry.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/fileentry.cpp
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "fileentry.h"
+#include "actionlist.h"
+
+#include
+#include
+
+FileEntry::FileEntry(AbstractModel *owner, const QUrl &url) : AbstractEntry(owner)
+, m_fileItem(nullptr)
+{
+ if (url.isValid()) {
+ m_fileItem = new KFileItem(url);
+ m_fileItem->determineMimeType();
+ }
+}
+
+FileEntry::~FileEntry()
+{
+ delete m_fileItem;
+}
+
+bool FileEntry::isValid() const
+{
+ return m_fileItem && (m_fileItem->isFile() || m_fileItem->isDir());
+}
+
+QIcon FileEntry::icon() const
+{
+ if (m_fileItem) {
+ return QIcon::fromTheme(m_fileItem->iconName(), QIcon::fromTheme(QStringLiteral("unknown")));
+ }
+
+ return QIcon::fromTheme(QStringLiteral("unknown"));
+}
+
+QString FileEntry::name() const
+{
+ if (m_fileItem) {
+ return m_fileItem->text();
+ }
+
+ return QString();
+}
+
+QString FileEntry::description() const
+{
+ if (m_fileItem) {
+ return m_fileItem->url().toString(QUrl::PreferLocalFile);
+ }
+
+ return QString();
+}
+
+QString FileEntry::id() const
+{
+ if (m_fileItem) {
+ return m_fileItem->url().toString();
+ }
+
+ return QString();
+}
+
+QUrl FileEntry::url() const
+{
+ if (m_fileItem) {
+ return m_fileItem->url();
+ }
+
+ return QUrl();
+}
+
+bool FileEntry::hasActions() const
+{
+ return m_fileItem && m_fileItem->isFile();
+}
+
+QVariantList FileEntry::actions() const
+{
+ if (m_fileItem) {
+ return Kicker::createActionListForFileItem(*m_fileItem);
+ }
+
+ return QVariantList();
+}
+
+bool FileEntry::run(const QString& actionId, const QVariant &argument)
+{
+ if (!m_fileItem) {
+ return false;
+ }
+
+ if (actionId.isEmpty()) {
+ new KRun(m_fileItem->url(), nullptr);
+
+ return true;
+ } else {
+ bool close = false;
+
+ if (Kicker::handleFileItemAction(*m_fileItem, actionId, argument, &close)) {
+ return close;
+ }
+ }
+
+ return false;
+}
diff --git a/applets/kicker/plugin/forwardingmodel.h b/applets/kicker/plugin/forwardingmodel.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/forwardingmodel.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef FORWARDINGMODEL_H
+#define FORWARDINGMODEL_H
+
+#include "abstractmodel.h"
+
+#include
+
+class ForwardingModel : public AbstractModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
+
+ public:
+ explicit ForwardingModel(QObject *parent = nullptr);
+ ~ForwardingModel() override;
+
+ QString description() const override;
+
+ QAbstractItemModel *sourceModel() const;
+ virtual void setSourceModel(QAbstractItemModel *sourceModel);
+
+ bool canFetchMore(const QModelIndex &parent) const override;
+ void fetchMore(const QModelIndex &parent) override;
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) override;
+
+ Q_INVOKABLE QString labelForRow(int row) override;
+
+ Q_INVOKABLE AbstractModel *modelForRow(int row) override;
+
+ AbstractModel* favoritesModel() override;
+
+ int separatorCount() const override;
+
+ public Q_SLOTS:
+ void reset();
+
+ Q_SIGNALS:
+ void sourceModelChanged() const;
+
+ protected:
+ QModelIndex indexToSourceIndex(const QModelIndex &index) const;
+
+ void connectSignals();
+ void disconnectSignals();
+
+ QPointer m_sourceModel;
+};
+
+#endif
diff --git a/applets/kicker/plugin/forwardingmodel.cpp b/applets/kicker/plugin/forwardingmodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/forwardingmodel.cpp
@@ -0,0 +1,265 @@
+/***************************************************************************
+ * Copyright (C) 2015 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "forwardingmodel.h"
+
+ForwardingModel::ForwardingModel(QObject *parent) : AbstractModel(parent)
+{
+}
+
+ForwardingModel::~ForwardingModel()
+{
+}
+
+QString ForwardingModel::description() const
+{
+ if (!m_sourceModel) {
+ return QString();
+ }
+
+ AbstractModel *abstractModel = qobject_cast(m_sourceModel);
+
+ if (!abstractModel) {
+ return QString();
+ }
+
+ return abstractModel->description();
+}
+
+QAbstractItemModel *ForwardingModel::sourceModel() const
+{
+ return m_sourceModel;
+}
+
+void ForwardingModel::setSourceModel(QAbstractItemModel *sourceModel)
+{
+ disconnectSignals();
+
+ beginResetModel();
+
+ m_sourceModel = sourceModel;
+
+ connectSignals();
+
+ endResetModel();
+
+ emit countChanged();
+ emit sourceModelChanged();
+ emit descriptionChanged();
+}
+
+bool ForwardingModel::canFetchMore(const QModelIndex &parent) const
+{
+ if (!m_sourceModel) {
+ return false;
+ }
+
+ return m_sourceModel->canFetchMore(indexToSourceIndex(parent));
+}
+
+void ForwardingModel::fetchMore(const QModelIndex &parent)
+{
+ if (m_sourceModel) {
+ m_sourceModel->fetchMore(indexToSourceIndex(parent));
+ }
+}
+
+QModelIndex ForwardingModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+
+ if (!m_sourceModel) {
+ return QModelIndex();
+ }
+
+ return createIndex(row, column);
+}
+
+QModelIndex ForwardingModel::parent(const QModelIndex &index) const
+{
+ Q_UNUSED(index)
+
+ return QModelIndex();
+}
+
+QVariant ForwardingModel::data(const QModelIndex &index, int role) const
+{
+ if (!m_sourceModel) {
+ return QVariant();
+ }
+
+ return m_sourceModel->data(indexToSourceIndex(index), role);
+}
+
+int ForwardingModel::rowCount(const QModelIndex &parent) const
+{
+ if (!m_sourceModel) {
+ return 0;
+ }
+
+ return m_sourceModel->rowCount(indexToSourceIndex(parent));
+}
+
+QModelIndex ForwardingModel::indexToSourceIndex(const QModelIndex& index) const
+{
+ if (!m_sourceModel || !index.isValid()) {
+ return QModelIndex();
+ }
+
+ return m_sourceModel->index(index.row(), index.column(),
+ index.parent().isValid() ? indexToSourceIndex(index.parent()) : QModelIndex());
+}
+
+bool ForwardingModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ if (!m_sourceModel) {
+ return false;
+ }
+
+ AbstractModel *abstractModel = qobject_cast(m_sourceModel);
+
+ if (!abstractModel) {
+ return false;
+ }
+
+ return abstractModel->trigger(row, actionId, argument);
+}
+
+QString ForwardingModel::labelForRow(int row)
+{
+ if (!m_sourceModel) {
+ return QString();
+ }
+
+ AbstractModel *abstractModel = qobject_cast(m_sourceModel);
+
+ if (!abstractModel) {
+ return QString();
+ }
+
+ return abstractModel->labelForRow(row);
+}
+
+AbstractModel* ForwardingModel::modelForRow(int row)
+{
+ if (!m_sourceModel) {
+ return nullptr;
+ }
+
+ AbstractModel *abstractModel = qobject_cast(m_sourceModel);
+
+ if (!abstractModel) {
+ return nullptr;
+ }
+
+ return abstractModel->modelForRow(row);
+}
+
+AbstractModel* ForwardingModel::favoritesModel()
+{
+ AbstractModel *sourceModel = qobject_cast(m_sourceModel);
+
+ if (sourceModel) {
+ return sourceModel->favoritesModel();
+ }
+
+ return AbstractModel::favoritesModel();
+}
+
+int ForwardingModel::separatorCount() const
+{
+ if (!m_sourceModel) {
+ return 0;
+ }
+
+ AbstractModel *abstractModel = qobject_cast(m_sourceModel);
+
+ if (!abstractModel) {
+ return 0;
+ }
+
+ return abstractModel->separatorCount();
+}
+
+void ForwardingModel::reset()
+{
+ beginResetModel();
+ endResetModel();
+
+ emit countChanged();
+ emit separatorCountChanged();
+}
+
+void ForwardingModel::connectSignals()
+{
+ if (!m_sourceModel) {
+ return;
+ }
+
+ connect(m_sourceModel, SIGNAL(destroyed()), this, SLOT(reset()));
+ connect(m_sourceModel.data(), &QAbstractItemModel::dataChanged,
+ this, &QAbstractItemModel::dataChanged,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsAboutToBeInserted,
+ this, &QAbstractItemModel::rowsAboutToBeInserted,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsAboutToBeMoved,
+ this, &QAbstractItemModel::rowsAboutToBeMoved,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &QAbstractItemModel::rowsAboutToBeRemoved,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::layoutAboutToBeChanged,
+ this, &QAbstractItemModel::layoutAboutToBeChanged,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsInserted,
+ this, &QAbstractItemModel::rowsInserted,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsInserted,
+ this, &AbstractModel::countChanged, Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsMoved,
+ this, &QAbstractItemModel::rowsMoved,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsRemoved,
+ this, &QAbstractItemModel::rowsRemoved,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::rowsRemoved,
+ this, &AbstractModel::countChanged, Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::modelAboutToBeReset,
+ this, &QAbstractItemModel::modelAboutToBeReset,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::modelReset,
+ this, &QAbstractItemModel::modelReset,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::modelReset,
+ this, &AbstractModel::countChanged,
+ Qt::UniqueConnection);
+ connect(m_sourceModel.data(), &QAbstractItemModel::layoutChanged,
+ this, &QAbstractItemModel::layoutChanged,
+ Qt::UniqueConnection);
+}
+
+void ForwardingModel::disconnectSignals()
+{
+ if (!m_sourceModel) {
+ return;
+ }
+
+ disconnect(m_sourceModel, nullptr, this, nullptr);
+}
diff --git a/dataengines/share/packagestructure/share_package.h b/applets/kicker/plugin/funnelmodel.h
rename from dataengines/share/packagestructure/share_package.h
rename to applets/kicker/plugin/funnelmodel.h
--- a/dataengines/share/packagestructure/share_package.h
+++ b/applets/kicker/plugin/funnelmodel.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright 2010 Artur Duque de Souza *
+ * Copyright (C) 2014 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,16 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#ifndef SHARE_PACKAGE_H
-#define SHARE_PACKAGE_H
+#ifndef FUNNELMODEL_H
+#define FUNNELMODEL_H
-#include
+#include "forwardingmodel.h"
-class SharePackage : public KPackage::PackageStructure
+class FunnelModel : public ForwardingModel
{
-public:
- explicit SharePackage(QObject *parent = nullptr, QVariantList args = QVariantList());
- void initPackage(KPackage::Package *package) override;
+ Q_OBJECT
+
+ public:
+ explicit FunnelModel(QObject *parent = nullptr);
+ ~FunnelModel() override;
+
+ void setSourceModel(QAbstractItemModel *model) override;
};
#endif
diff --git a/applets/kicker/plugin/funnelmodel.cpp b/applets/kicker/plugin/funnelmodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/funnelmodel.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Eike Hein *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "funnelmodel.h"
+
+FunnelModel::FunnelModel(QObject *parent) : ForwardingModel(parent)
+{
+}
+
+FunnelModel::~FunnelModel()
+{
+}
+
+void FunnelModel::setSourceModel(QAbstractItemModel *model)
+{
+ if (model && m_sourceModel == model) {
+ return;
+ }
+
+ if (!model) {
+ reset();
+
+ return;
+ }
+
+ connect(model, SIGNAL(destroyed(QObject*)), this, SLOT(reset()));
+
+ if (!m_sourceModel) {
+ beginResetModel();
+
+ m_sourceModel = model;
+
+ connectSignals();
+
+ endResetModel();
+
+ emit countChanged();
+
+ emit sourceModelChanged();
+ emit descriptionChanged();
+
+ return;
+ }
+
+ int oldCount = m_sourceModel->rowCount();
+ int newCount = model->rowCount();
+
+ auto setNewModel = [this, model]() {
+ disconnectSignals();
+ m_sourceModel = model;
+ connectSignals();
+ };
+
+ if (newCount > oldCount) {
+ beginInsertRows(QModelIndex(), oldCount, newCount - 1);
+ setNewModel();
+ endInsertRows();
+ } else if (newCount < oldCount) {
+ if (newCount == 0) {
+ beginResetModel();
+ setNewModel();
+ endResetModel();
+ } else {
+ beginRemoveRows(QModelIndex(), newCount, oldCount - 1);
+ setNewModel();
+ endRemoveRows();
+ }
+ } else {
+ setNewModel();
+ }
+
+ if (newCount > 0) {
+ emit dataChanged(index(0, 0), index(qMin(oldCount, newCount) - 1, 0));
+ }
+
+ if (oldCount != newCount) {
+ emit countChanged();
+ }
+
+ emit sourceModelChanged();
+ emit descriptionChanged();
+}
diff --git a/applets/kicker/plugin/kastatsfavoritesmodel.h b/applets/kicker/plugin/kastatsfavoritesmodel.h
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/kastatsfavoritesmodel.h
@@ -0,0 +1,116 @@
+/***************************************************************************
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * Copyright (C) 2016-2017 by Ivan Cukic *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#ifndef FAVORITESMODEL_H
+#define FAVORITESMODEL_H
+
+#include "placeholdermodel.h"
+
+#include
+
+#include
+#include
+
+class PlaceholderModel;
+
+namespace KActivities {
+ class Consumer;
+namespace Stats {
+namespace Terms {
+ class Activity;
+} // namespace Terms
+} // namespace Stats
+} // namespace KActivities
+
+class KAStatsFavoritesModel : public PlaceholderModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(QStringList favorites READ favorites WRITE setFavorites NOTIFY favoritesChanged)
+ Q_PROPERTY(int maxFavorites READ maxFavorites WRITE setMaxFavorites NOTIFY maxFavoritesChanged)
+
+ Q_PROPERTY(QObject* activities READ activities CONSTANT)
+
+ public:
+ explicit KAStatsFavoritesModel(QObject *parent = nullptr);
+ ~KAStatsFavoritesModel() override;
+
+ QString description() const override;
+
+ Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument) override;
+
+ bool enabled() const;
+ void setEnabled(bool enable);
+
+ QStringList favorites() const;
+ void setFavorites(const QStringList &favorites);
+
+ int maxFavorites() const;
+ void setMaxFavorites(int max);
+
+ Q_INVOKABLE bool isFavorite(const QString &id) const;
+
+ Q_INVOKABLE void addFavorite(const QString &id, int index = -1);
+ Q_INVOKABLE void removeFavorite(const QString &id);
+
+ Q_INVOKABLE void addFavoriteTo(const QString &id, const QString &activityId, int index = -1);
+ Q_INVOKABLE void removeFavoriteFrom(const QString &id, const QString &activityId);
+
+ Q_INVOKABLE void setFavoriteOn(const QString &id, const QString &activityId);
+
+ Q_INVOKABLE void portOldFavorites(const QStringList &ids);
+
+ Q_INVOKABLE QStringList linkedActivitiesFor(const QString &id) const;
+
+ Q_INVOKABLE void moveRow(int from, int to);
+
+ Q_INVOKABLE void initForClient(const QString &client);
+
+ QObject *activities() const;
+ Q_INVOKABLE QString activityNameForId(const QString &activityId) const;
+
+ AbstractModel* favoritesModel() override;
+
+ public Q_SLOTS:
+ void refresh() override;
+
+ Q_SIGNALS:
+ void enabledChanged() const;
+ void favoritesChanged() const;
+ void maxFavoritesChanged() const;
+
+ private:
+ class Private;
+ Private * d;
+
+ AbstractEntry *favoriteFromId(const QString &id) const;
+
+ void addFavoriteTo(const QString &id, const KActivities::Stats::Terms::Activity &activityId, int index = -1);
+ void removeFavoriteFrom(const QString &id, const KActivities::Stats::Terms::Activity &activityId);
+
+ bool m_enabled;
+
+ int m_maxFavorites;
+
+ KActivities::Consumer *m_activities;
+};
+
+#endif
diff --git a/applets/kicker/plugin/kastatsfavoritesmodel.cpp b/applets/kicker/plugin/kastatsfavoritesmodel.cpp
new file mode 100644
--- /dev/null
+++ b/applets/kicker/plugin/kastatsfavoritesmodel.cpp
@@ -0,0 +1,719 @@
+/***************************************************************************
+ * Copyright (C) 2014-2015 by Eike Hein *
+ * Copyright (C) 2016-2017 by Ivan Cukic *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
+ ***************************************************************************/
+
+#include "kastatsfavoritesmodel.h"
+#include "appentry.h"
+#include "contactentry.h"
+#include "fileentry.h"
+#include "actionlist.h"
+#include "debug.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace KAStats = KActivities::Stats;
+
+using namespace KAStats;
+using namespace KAStats::Terms;
+
+#define AGENT_APPLICATIONS QStringLiteral("org.kde.plasma.favorites.applications")
+#define AGENT_CONTACTS QStringLiteral("org.kde.plasma.favorites.contacts")
+#define AGENT_DOCUMENTS QStringLiteral("org.kde.plasma.favorites.documents")
+
+QString agentForUrl(const QString &url)
+{
+ return url.startsWith(QLatin1String("ktp:"))
+ ? AGENT_CONTACTS
+ : url.startsWith(QLatin1String("preferred:"))
+ ? AGENT_APPLICATIONS
+ : url.startsWith(QLatin1String("applications:"))
+ ? AGENT_APPLICATIONS
+ : (url.startsWith(QLatin1Char('/')) && !url.endsWith(QLatin1String(".desktop")))
+ ? AGENT_DOCUMENTS
+ : (url.startsWith(QLatin1String("file:/")) && !url.endsWith(QLatin1String(".desktop")))
+ ? AGENT_DOCUMENTS
+ // use applications as the default
+ : AGENT_APPLICATIONS;
+}
+
+class KAStatsFavoritesModel::Private: public QAbstractListModel {
+public:
+ class NormalizedId {
+ public:
+ NormalizedId()
+ {
+ }
+
+ NormalizedId(const Private *parent, const QString &id)
+ {
+ if (id.isEmpty()) return;
+
+ QSharedPointer entry = nullptr;
+
+ if (parent->m_itemEntries.contains(id)) {
+ entry = parent->m_itemEntries[id];
+ } else {
+ // This entry is not cached - it is temporary,
+ // so let's clean up when we exit this function
+ entry = parent->entryForResource(id);
+ }
+
+ if (!entry || !entry->isValid()) {
+ qWarning() << "Entry is not valid" << id << entry;
+ m_id = id;
+ return;
+ }
+
+ const auto url = entry->url();
+
+ qCDebug(KICKER_DEBUG) << "Original id is: " << id << ", and the url is" << url;
+
+ // Preferred applications need special handling
+ if (entry->id().startsWith(QLatin1String("preferred:"))) {
+ m_id = entry->id();
+ return;
+ }
+
+ // If this is an application, use the applications:-format url
+ auto appEntry = dynamic_cast(entry.data());
+ if (appEntry && !appEntry->menuId().isEmpty()) {
+ m_id = QLatin1String("applications:") + appEntry->menuId();
+ return;
+ }
+
+ // We want to resolve symbolic links not to have two paths
+ // refer to the same .desktop file
+ if (url.isLocalFile()) {
+ QFileInfo file(url.toLocalFile());
+
+ if (file.exists()) {
+ m_id = QUrl::fromLocalFile(file.canonicalFilePath()).toString();
+ return;
+ }
+ }
+
+ // If this is a file, we should have already covered it
+ if (url.scheme() == QLatin1String("file")) {
+ return;
+ }
+
+ m_id = url.toString();
+ }
+
+ const QString& value() const
+ {
+ return m_id;
+ }
+
+ bool operator==(const NormalizedId &other) const
+ {
+ return m_id == other.m_id;
+ }
+
+ private:
+ QString m_id;
+ };
+
+ NormalizedId normalizedId(const QString &id) const
+ {
+ return NormalizedId(this, id);
+ }
+
+ QSharedPointer entryForResource(const QString &resource) const
+ {
+ using SP = QSharedPointer;
+
+ const auto agent =
+ agentForUrl(resource);
+
+ if (agent == AGENT_CONTACTS) {
+ return SP(new ContactEntry(q, resource));
+
+ } else if (agent == AGENT_DOCUMENTS) {
+ if (resource.startsWith(QLatin1String("/"))) {
+ return SP(new FileEntry(q, QUrl::fromLocalFile(resource)));
+ } else {
+ return SP(new FileEntry(q, QUrl(resource)));
+ }
+
+ } else if (agent == AGENT_APPLICATIONS) {
+ if (resource.startsWith(QLatin1String("applications:"))) {
+ return SP(new AppEntry(q, resource.mid(13)));
+ } else {
+ return SP(new AppEntry(q, resource));
+ }
+
+ } else {
+ return {};
+ }
+ }
+
+ Private(KAStatsFavoritesModel *parent, QString clientId)
+ : q(parent)
+ , m_query(
+ LinkedResources
+ | Agent {
+ AGENT_APPLICATIONS,
+ AGENT_CONTACTS,
+ AGENT_DOCUMENTS
+ }
+ | Type::any()
+ | Activity::current()
+ | Activity::global()
+ | Limit::all()
+ )
+ , m_watcher(m_query)
+ , m_clientId(clientId)
+ {
+ // Connecting the watcher
+ connect(&m_watcher, &ResultWatcher::resultLinked,
+ [this] (const QString &resource) {
+ addResult(resource, -1);
+ });
+
+ connect(&m_watcher, &ResultWatcher::resultUnlinked,
+ [this] (const QString &resource) {
+ removeResult(resource);
+ });
+
+ // Loading the items order
+ const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc"));
+
+ // We want first to check whether we have an ordering for this activity.
+ // If not, we will try to get a global one for this applet
+
+ const QString thisGroupName =
+ QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + m_activities.currentActivity();
+ const QString globalGroupName =
+ QStringLiteral("Favorites-") + clientId + QStringLiteral("-global");
+
+ KConfigGroup thisCfgGroup(cfg, thisGroupName);
+ KConfigGroup globalCfgGroup(cfg, globalGroupName);
+
+ QStringList ordering =
+ thisCfgGroup.readEntry("ordering", QStringList()) +
+ globalCfgGroup.readEntry("ordering", QStringList());
+
+ qCDebug(KICKER_DEBUG) << "Loading the ordering " << ordering;
+
+ // Loading the results without emitting any model signals
+ qCDebug(KICKER_DEBUG) << "Query is" << m_query;
+ ResultSet results(m_query);
+
+ for (const auto& result: results) {
+ qCDebug(KICKER_DEBUG) << "Got " << result.resource() << " -->";
+ addResult(result.resource(), -1, false);
+ }
+
+ // Normalizing all the ids
+ std::transform(ordering.begin(), ordering.end(), ordering.begin(),
+ [&] (const QString &item) {
+ return normalizedId(item).value();
+ });
+
+ // Sorting the items in the cache
+ std::sort(m_items.begin(), m_items.end(),
+ [&] (const NormalizedId &left, const NormalizedId &right) {
+ auto leftIndex = ordering.indexOf(left.value());
+ auto rightIndex = ordering.indexOf(right.value());
+
+ return (leftIndex == -1 && rightIndex == -1) ?
+ left.value() < right.value() :
+
+ (leftIndex == -1) ?
+ false :
+
+ (rightIndex == -1) ?
+ true :
+
+ // otherwise
+ leftIndex < rightIndex;
+ });
+
+ // Debugging:
+ QVector itemStrings(m_items.size());
+ std::transform(m_items.cbegin(), m_items.cend(), itemStrings.begin(),
+ [] (const NormalizedId &item) {
+ return item.value();
+ });
+ qCDebug(KICKER_DEBUG) << "After ordering: " << itemStrings;
+ }
+
+ void addResult(const QString &_resource, int index, bool notifyModel = true)
+ {
+ // We want even files to have a proper URL
+ const auto resource =
+ _resource.startsWith(QLatin1Char('/')) ? QUrl::fromLocalFile(_resource).toString() : _resource;
+
+ qCDebug(KICKER_DEBUG) << "Adding result" << resource << "already present?" << m_itemEntries.contains(resource);
+
+ if (m_itemEntries.contains(resource)) return;
+
+ auto entry = entryForResource(resource);
+
+ if (!entry || !entry->isValid()) {
+ qCDebug(KICKER_DEBUG) << "Entry is not valid!";
+ return;
+ }
+
+ if (index == -1) {
+ index = m_items.count();
+ }
+
+ if (notifyModel) {
+ beginInsertRows(QModelIndex(), index, index);
+ }
+
+ auto url = entry->url();
+
+ m_itemEntries[resource]
+ = m_itemEntries[entry->id()]
+ = m_itemEntries[url.toString()]
+ = m_itemEntries[url.toLocalFile()]
+ = entry;
+
+ auto normalized = normalizedId(resource);
+ m_items.insert(index, normalized);
+ m_itemEntries[normalized.value()] = entry;
+
+ if (notifyModel) {
+ endInsertRows();
+ saveOrdering();
+ }
+ }
+
+ void removeResult(const QString &resource)
+ {
+ auto normalized = normalizedId(resource);
+
+ // If we know this item will not really be removed,
+ // but only that activities it is on have changed,
+ // lets leave it
+ if (m_ignoredItems.contains(normalized.value())) {
+ m_ignoredItems.removeAll(normalized.value());
+ return;
+ }
+
+ qCDebug(KICKER_DEBUG) << "Removing result" << resource;
+
+ auto index = m_items.indexOf(normalizedId(resource));
+
+ if (index == -1) return;
+
+ beginRemoveRows(QModelIndex(), index, index);
+ auto entry = m_itemEntries[resource];
+ m_items.removeAt(index);
+
+ // Removing the entry from the cache
+ QMutableHashIterator> i(m_itemEntries);
+ while (i.hasNext()) {
+ i.next();
+ if (i.value() == entry) {
+ i.remove();
+ }
+ }
+
+ endRemoveRows();
+ }
+
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ if (parent.isValid()) return 0;
+
+ return m_items.count();
+ }
+
+ QVariant data(const QModelIndex &item,
+ int role = Qt::DisplayRole) const override
+ {
+ if (item.parent().isValid()) return QVariant();
+
+ const auto index = item.row();
+
+ const auto entry = m_itemEntries[m_items[index].value()];
+
+ return entry == nullptr ? QVariant()
+ : role == Qt::DisplayRole ? entry->name()
+ : role == Qt::DecorationRole ? entry->icon()
+ : role == Kicker::DescriptionRole ? entry->description()
+ : role == Kicker::FavoriteIdRole ? entry->id()
+ : role == Kicker::UrlRole ? entry->url()
+ : role == Kicker::HasActionListRole ? entry->hasActions()
+ : role == Kicker::ActionListRole ? entry->actions()
+ : QVariant();
+ }
+
+ bool trigger(int row, const QString &actionId, const QVariant &argument)
+ {
+ if (row < 0 || row >= rowCount()) {
+ return false;
+ }
+
+ const QString id = data(index(row, 0), Kicker::UrlRole).toString();
+
+ return m_itemEntries.contains(id)
+ ? m_itemEntries[id]->run(actionId, argument)
+ : false;
+ }
+
+ void move(int from, int to)
+ {
+ if (from < 0) return;
+ if (from >= m_items.count()) return;
+ if (to < 0) return;
+ if (to >= m_items.count()) return;
+
+ if (from == to) return;
+
+ const int modelTo = to + (to > from ? 1 : 0);
+
+ if (q->beginMoveRows(QModelIndex(), from, from,
+ QModelIndex(), modelTo)) {
+ m_items.move(from, to);
+ q->endMoveRows();
+
+ qCDebug(KICKER_DEBUG) << "Save ordering (from Private::move) -->";
+ saveOrdering();
+ }
+ }
+
+ void saveOrdering()
+ {
+ QStringList ids;
+
+ for (const auto& item: m_items) {
+ ids << item.value();
+ }
+
+ qCDebug(KICKER_DEBUG) << "Save ordering (from Private::saveOrdering) -->";
+ saveOrdering(ids, m_clientId, m_activities.currentActivity());
+ }
+
+ static void saveOrdering(const QStringList &ids, const QString &clientId, const QString ¤tActivity)
+ {
+ const auto cfg = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerd-statsrc"));
+
+ QStringList activities {
+ currentActivity,
+ QStringLiteral("global")
+ };
+
+ qCDebug(KICKER_DEBUG) << "Saving ordering for" << currentActivity << "and global" << ids;
+
+ for (const auto& activity: activities) {
+ const QString groupName =
+ QStringLiteral("Favorites-") + clientId + QStringLiteral("-") + activity;
+
+ KConfigGroup cfgGroup(cfg, groupName);
+
+ cfgGroup.writeEntry("ordering", ids);
+ }
+
+ cfg->sync();
+ }
+
+ KAStatsFavoritesModel *const q;
+ KActivities::Consumer m_activities;
+ Query m_query;
+ ResultWatcher m_watcher;
+ QString m_clientId;
+
+ QVector m_items;
+ QHash> m_itemEntries;
+ QStringList m_ignoredItems;
+};
+
+KAStatsFavoritesModel::KAStatsFavoritesModel(QObject *parent)
+: PlaceholderModel(parent)
+, d(nullptr) // we have no client id yet
+, m_enabled(true)
+, m_maxFavorites(-1)
+, m_activities(new KActivities::Consumer(this))
+{
+ connect(m_activities, &KActivities::Consumer::currentActivityChanged,
+ this, [&] (const QString ¤tActivity) {
+ qCDebug(KICKER_DEBUG) << "Activity just got changed to" << currentActivity;
+ Q_UNUSED(currentActivity);
+ if (d) {
+ auto clientId = d->m_clientId;
+ initForClient(clientId);
+ }
+ });
+}
+
+KAStatsFavoritesModel::~KAStatsFavoritesModel()
+{
+ delete d;
+}
+
+void KAStatsFavoritesModel::initForClient(const QString &clientId)
+{
+ qCDebug(KICKER_DEBUG) << "initForClient" << clientId;
+
+ setSourceModel(nullptr);
+ delete d;
+ d = new Private(
+ this,
+ clientId
+ );
+
+ setSourceModel(d);
+}
+
+QString KAStatsFavoritesModel::description() const
+{
+ return i18n("Favorites");
+}
+
+bool KAStatsFavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument)
+{
+ return d && d->trigger(row, actionId, argument);
+}
+
+bool KAStatsFavoritesModel::enabled() const
+{
+ return m_enabled;
+}
+
+int KAStatsFavoritesModel::maxFavorites() const
+{
+ return m_maxFavorites;
+}
+
+void KAStatsFavoritesModel::setMaxFavorites(int max)
+{
+ Q_UNUSED(max);
+}
+
+void KAStatsFavoritesModel::setEnabled(bool enable)
+{
+ if (m_enabled != enable) {
+ m_enabled = enable;
+
+ emit enabledChanged();
+ }
+}
+
+QStringList KAStatsFavoritesModel::favorites() const
+{
+ qWarning() << "KAStatsFavoritesModel::favorites returns nothing, it is here just to keep the API backwards-compatible";
+ return QStringList();
+}
+
+void KAStatsFavoritesModel::setFavorites(const QStringList& favorites)
+{
+ Q_UNUSED(favorites);
+ qWarning() << "KAStatsFavoritesModel::setFavorites is ignored";
+}
+
+bool KAStatsFavoritesModel::isFavorite(const QString &id) const
+{
+ return d && d->m_itemEntries.contains(id);
+}
+
+void KAStatsFavoritesModel::portOldFavorites(const QStringList &ids)
+{
+ if (!d) return;
+ qCDebug(KICKER_DEBUG) << "portOldFavorites" << ids;
+
+ const QString activityId = QStringLiteral(":global");
+ std::for_each(ids.begin(), ids.end(), [&] (const QString &id) {
+ addFavoriteTo(id, activityId);
+ });
+
+ // Resetting the model
+ auto clientId = d->m_clientId;
+ setSourceModel(nullptr);
+ delete d;
+ d = nullptr;
+
+ qCDebug(KICKER_DEBUG) << "Save ordering (from portOldFavorites) -->";
+ Private::saveOrdering(ids, clientId, m_activities->currentActivity());
+
+ QTimer::singleShot(500,
+ std::bind(&KAStatsFavoritesModel::initForClient, this, clientId));
+}
+
+void KAStatsFavoritesModel::addFavorite(const QString &id, int index)
+{
+ qCDebug(KICKER_DEBUG) << "addFavorite" << id << index << " -->";
+ addFavoriteTo(id, QStringLiteral(":global"), index);
+}
+
+void KAStatsFavoritesModel::removeFavorite(const QString &id)
+{
+ qCDebug(KICKER_DEBUG) << "removeFavorite" << id << " -->";
+ removeFavoriteFrom(id, QStringLiteral(":any"));
+}
+
+void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const QString &activityId, int index)
+{
+ qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activityId << index << " -->";
+ addFavoriteTo(id, Activity(activityId), index);
+}
+
+void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const QString &activityId)
+{
+ qCDebug(KICKER_DEBUG) << "removeFavoriteFrom" << id << activityId << " -->";
+ removeFavoriteFrom(id, Activity(activityId));
+}
+
+void KAStatsFavoritesModel::addFavoriteTo(const QString &id, const Activity &activity, int index)
+{
+ if (!d || id.isEmpty()) return;
+
+ Q_ASSERT(!activity.values.isEmpty());
+
+ setDropPlaceholderIndex(-1);
+
+ QStringList matchers { d->m_activities.currentActivity(), QStringLiteral(":global"), QStringLiteral(":current") };
+ if (std::find_first_of(activity.values.cbegin(), activity.values.cend(),
+ matchers.cbegin(), matchers.cend()) != activity.values.cend()) {
+ d->addResult(id, index);
+ }
+
+ const auto url = d->normalizedId(id).value();
+
+ qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << index << url << " (actual)";
+
+ if (url.isEmpty()) return;
+
+ d->m_watcher.linkToActivity(QUrl(url), activity,
+ Agent(agentForUrl(url)));
+}
+
+void KAStatsFavoritesModel::removeFavoriteFrom(const QString &id, const Activity &activity)
+{
+ if (!d || id.isEmpty()) return;
+
+ const auto url = d->normalizedId(id).value();
+
+ Q_ASSERT(!activity.values.isEmpty());
+
+ qCDebug(KICKER_DEBUG) << "addFavoriteTo" << id << activity << url << " (actual)";
+
+ if (url.isEmpty()) return;
+
+ d->m_watcher.unlinkFromActivity(QUrl(url), activity,
+ Agent(agentForUrl(url)));
+}
+
+void KAStatsFavoritesModel::setFavoriteOn(const QString &id, const QString &activityId)
+{
+ if (!d || id.isEmpty()) return;
+
+ const auto url = d->normalizedId(id).value();
+
+ qCDebug(KICKER_DEBUG) << "setFavoriteOn" << id << activityId << url << " (actual)";
+
+ qCDebug(KICKER_DEBUG) << "%%%%%%%%%%% Activity is" << activityId;
+ if (activityId.isEmpty() || activityId == QLatin1String(":any") ||
+ activityId == QLatin1String(":global") ||
+ activityId == m_activities->currentActivity()) {
+ d->m_ignoredItems << url;
+ }
+
+ d->m_watcher.unlinkFromActivity(QUrl(url), Activity::any(),
+ Agent(agentForUrl(url)));
+ d->m_watcher.linkToActivity(QUrl(url), activityId,
+ Agent(agentForUrl(url)));
+}
+
+void KAStatsFavoritesModel::moveRow(int from, int to)
+{
+ if (!d) return;
+
+ d->move(from, to);
+}
+
+AbstractModel *KAStatsFavoritesModel::favoritesModel()
+{
+ return this;
+}
+
+void KAStatsFavoritesModel::refresh()
+{
+}
+
+QObject *KAStatsFavoritesModel::activities() const
+{
+ return m_activities;
+}
+
+QString KAStatsFavoritesModel::activityNameForId(const QString &activityId) const
+{
+ // It is safe to use a short-lived object here,
+ // we are always synced with KAMD in plasma
+ KActivities::Info info(activityId);
+ return info.name();
+}
+
+QStringList KAStatsFavoritesModel::linkedActivitiesFor(const QString &id) const
+{
+ if (!d) {
+ qCDebug(KICKER_DEBUG) << "Linked for" << id << "is empty, no Private instance";
+ return {};
+ }
+
+ auto url = d->normalizedId(id).value();
+
+ if (url.startsWith(QLatin1String("file:"))) {
+ url = QUrl(url).toLocalFile();
+ }
+
+ if (url.isEmpty()) {
+ qCDebug(KICKER_DEBUG) << "The url for" << id << "is empty";
+ return {};
+ }
+
+ auto query = LinkedResources
+ | Agent {
+ AGENT_APPLICATIONS,
+ AGENT_CONTACTS,
+ AGENT_DOCUMENTS
+ }
+ | Type::any()
+ | Activity::any()
+ | Url(url)
+ | Limit::all();
+
+ ResultSet results(query);
+
+ for (const auto &result: results) {
+ qCDebug(KICKER_DEBUG) << "Returning" << result.linkedActivities() << "for" << id << url;
+ return result.linkedActivities();
+ }
+
+ qCDebug(KICKER_DEBUG) << "Returning empty list of activities for" << id << url;
+ return {};
+}
+
diff --git a/dataengines/share/backends/wstaw/contents/code/main.js b/applets/kicker/plugin/kickerplugin.h
rename from dataengines/share/backends/wstaw/contents/code/main.js
rename to applets/kicker/plugin/kickerplugin.h
--- a/dataengines/share/backends/wstaw/contents/code/main.js
+++ b/applets/kicker/plugin/kickerplugin.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2010 by Artur Duque de Souza *
+ * Copyright (C) 2014 by Eike Hein *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@@ -17,22 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-function url() {
- return "http://wstaw.org";
-}
+#ifndef KICKERPLUGIN_H
+#define KICKERPLUGIN_H
-function contentKey() {
- return "pic";
-}
+#include
+#include